public class CharacterSearchService {

    private static final String TAG = "CharacterSearchService";

    private static int ANIMATION_DURATION = 400;
    private static int ANIMATION_DISTANCE = 400;
    private static int THREADS_NUMBER = 0;

    private EditText mQueryContainer;
    private TextView mNoResultsFound;
    private ActivityTemplate mActivity;
    private RecyclerView mSearchResults;
    private String mLastMatchingQuery;
    private Boolean isValidQuery = Boolean.TRUE;

    @Inject
    CharacterUIService characterUIService;
    @Inject
    Postman mPostman;
    @Inject
    LoaderManager mLoaderManager;
    @Inject
    NotificationManager mNotificationManager;

    public CharacterSearchService() {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
        }
    }

    public TaskExecutor event0000Handler = new TaskExecutor() {
        @Override
        public void executeOnSuccessTask(Object response) {
            CharacterSearchService.THREADS_NUMBER--;
            mLoaderManager.hideIndeterminateProgressBar(CharacterSearchService.this.mActivity);
            Gson gson = new Gson();
            PayloadWrapper payloadWrapper = gson.fromJson(response.toString(), PayloadWrapper.class);
            List<Character> results = payloadWrapper.getData().getResults();

            if (results.size() > 0) {
                CharacterSearchService.this.mLastMatchingQuery = CharacterSearchService.this.mQueryContainer.getText().toString();
                CharacterSearchService.this.hideNoResultsFoundMessage();
                CharacterSearchService.this.characterUIService.updateResultList(results);
            } else {
                CharacterSearchService.this.showNoResultsFoundMessage();
            }
        }

        @Override
        public void executeOnErrorTask(Object payload) {
            CharacterSearchService.this.THREADS_NUMBER--;
            if (payload != null) {
                mNotificationManager.showErrorPopUp(CharacterSearchService.this.mActivity, payload.toString());
                Log.e(TAG, payload.toString());
            }
        }
    };

    private void hideNoResultsFoundMessage() {
        if (this.mNoResultsFound.getVisibility() == TextView.VISIBLE) {
            this.mSearchResults.animate().translationXBy(-ANIMATION_DISTANCE).alpha(1).setDuration(ANIMATION_DURATION).setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    CharacterSearchService.this.mNoResultsFound.setVisibility(TextView.GONE);
                }

                @Override
                public void onAnimationEnd(Animator animation) {

                }

                @Override
                public void onAnimationCancel(Animator animation) {

                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });
        }
    }

    private void showNoResultsFoundMessage() {
        this.updateUnmachedQueryText();
        if (this.mNoResultsFound.getVisibility() == TextView.GONE) {
            this.mSearchResults.animate().translationXBy(ANIMATION_DISTANCE).alpha(0).setDuration(ANIMATION_DURATION).setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    CharacterSearchService.this.mNoResultsFound.setVisibility(TextView.VISIBLE);
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                }

                @Override
                public void onAnimationRepeat(Animator animation) {
                }
            });
        }
    }

    private void updateUnmachedQueryText() {
        String label = CharacterSearchService.this.mActivity.getString(R.string.rsc_search_result_not_found);
        label = label.replace("PLACEHOLDER", Html.fromHtml("<b>".concat(this.mQueryContainer.getText().toString()).concat("</b>")));
        CharacterSearchService.this.mNoResultsFound.setText(label);
    }

    public void initSearchInterface(ActivityTemplate activity) {
        this.mSearchResults = this.characterUIService.loadSearchResults(activity);
        NotificationCenter.RegistrationCenter.registerForEvent(EventCatalog.e0000, event0000Handler);
        this.mActivity = activity;
        this.mQueryContainer = (EditText) this.mActivity.getToolbarService().getToolbar().findViewById(R.id.query_container);
        this.mNoResultsFound = (TextView) this.mActivity.findViewById(R.id.result_not_found);
        this.addQueryListener();
    }

    public void onTextChanged(String query) {
        if (this.mNoResultsFound.getVisibility() == TextView.GONE || query.equals(this.mLastMatchingQuery)) {
            if (null != query && !"".equals(query)) {
                CharacterSearchService.this.mActivity.getToolbarService().prepareNavigationBehavior(R.drawable.ic_clear, CharacterSearchService.this.clearQuery);
                if (CharacterSearchService.THREADS_NUMBER <= CharacterSearchService.this.mActivity.getResources().getInteger(R.integer.MARVEL_API_PARAM_MAX_REQUESTS)) {
                    mLoaderManager.showIndeterminateProgressBar(CharacterSearchService.this.mActivity);
                    CharacterSearchService.THREADS_NUMBER++;
                    this.mPostman.cancelRequests();
                    this.mPostman.listCharacters(CharacterSearchService.this.mActivity, query, 15, 0);
                }
            } else {
                CharacterSearchService.this.mActivity.getToolbarService().prepareNavigationBehavior(R.drawable.ic_arrow_left_white_24dp, CharacterSearchService.this.finishActivity);
            }
        } else if (query.equals("")) {
            this.hideNoResultsFoundMessage();
        } else {
            this.updateUnmachedQueryText();
        }
    }

    private ToolbarManager.OnIconClicked finishActivity = new ToolbarManager.OnIconClicked() {
        @Override
        public void executeTask() {
            CharacterSearchService.this.mActivity.finish();
        }
    };

    private ToolbarManager.OnIconClicked clearQuery = new ToolbarManager.OnIconClicked() {
        @Override
        public void executeTask() {
            CharacterSearchService.this.mQueryContainer.setText("");
        }
    };

    private void addQueryListener() {
        this.mQueryContainer.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(final CharSequence s, int start, int before, int count) {
                CharacterSearchService.this.onTextChanged(CharacterSearchService.this.mQueryContainer.getText().toString());
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }

} public class CharacterUIService {

    private static final String TAG = "CharacterUIService";

    private AdapterCharacter mAdapter;
    private AdapterCharacter mSearchAdapter;
    private ActivityTemplate mHomepageActivity;

    @Inject
    NotificationManager mNotificationManager;
    @Inject
    App mApplication;
    @Inject
    StaticResourceProvider mResources;
    @Inject
    Postman mPostman;
    @Inject
    RecyclerViewUtil mRecyclerViewUtil;
    @Inject
    LoaderManager mLoaderManager;

    public CharacterUIService() {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
        }
        this.mAdapter = new AdapterCharacter(new ArrayList<Character>(), LayoutType.LARGE_VERTICAL);
    }

    public TaskExecutor event0000Handler = new TaskExecutor() {
        @Override
        public void executeOnSuccessTask(Object response) {
            if (CharacterUIService.this.mHomepageActivity.isRunning()) {
                mLoaderManager.hideCircularProgressBar(CharacterUIService.this.mHomepageActivity);
                ActivityHomepage.LOADING = Boolean.FALSE;
                Gson gson = new Gson();
                PayloadWrapper payloadWrapper = gson.fromJson(response.toString(), PayloadWrapper.class);
                CharacterUIService.this.updateCharacterList(payloadWrapper.getData().getResults());
            }
        }

        @Override
        public void executeOnErrorTask(Object payload) {
            if (payload != null) {
                mNotificationManager.showErrorPopUp(CharacterUIService.this.mHomepageActivity, payload.toString());
                Log.e(TAG, payload.toString());
            }
        }
    };

    private void updateCharacterList(List<Character> characters) {
        if (null != this.mAdapter) {
            if (this.mAdapter.getItemCount() == 0) {
                this.mAdapter.setCharacter(characters);
                this.mAdapter.notifyDataSetChanged();
            } else {
                this.mAdapter.setCharacter(characters);
                this.mAdapter.notifyCharacterRangeInserted(this.mApplication.getContext().getResources().getInteger(R.integer.MARVEL_API_PARAM_DEFAULT_LIMIT));
            }
        }
    }

    public void updateResultList(List<Character> characters) {
        CharacterUIService.this.mSearchAdapter.setDataset(characters);
        CharacterUIService.this.mSearchAdapter.notifyDataSetChanged();
    }

    public RecyclerView loadSearchResults(final AppCompatActivity activity) {
        this.mSearchAdapter = new AdapterCharacter(new ArrayList<Character>(), LayoutType.SMALL_HORIZONTAL);

        return mRecyclerViewUtil.initListItems(
                activity,
                R.id.characters,
                LinearLayoutManager.VERTICAL,
                new RecyclerViewUtil.RecyclerViewOnItemClick(activity,
                        new RecyclerViewUtil.RecyclerViewOnItemClick.OnItemClickListener() {
                            @Override
                            public void onItemClick(View view, int position) {
                                Intent intent = new Intent(activity, ActivityCharacter.class);
                                intent.putExtra(Constants.ActivityCommunication.ACTCOMM_CHARACTER, CharacterUIService.this.mSearchAdapter.getCharacter(position));
                                activity.startActivity(intent);
                            }
                        }
                ),
                this.mSearchAdapter
        );
    }

    public void loadCharacters(final ActivityTemplate activity) {
        this.mHomepageActivity = activity;

        this.mAdapter.registerSubscriber(new Subscriber() {
            @Override
            public void onEventListener(Object... params) {
                if (params.length == 1) {
                    try {
                        if ((int) params[0] == (CharacterUIService.this.mAdapter.getItemCount() - 1) && !ActivityHomepage.LOADING) {
                            ActivityHomepage.LOADING = Boolean.TRUE;

                            mNotificationManager.showSnackAlert(
                                    activity,
                                    activity.findViewById(R.id.characters_coordinator_wrapper),
                                    activity.getString(R.string.rsc_homepage_loading_characters)
                            );

                            mPostman.listCharacters(
                                    activity,
                                    "",
                                    activity.getResources().getInteger(R.integer.MARVEL_API_PARAM_DEFAULT_LIMIT),
                                    CharacterUIService.this.mAdapter.getItemCount()
                            );
                        }
                    } catch (Exception e) {
                        Log.e(TAG, e.getMessage());
                    }
                }
            }
        });

        mRecyclerViewUtil.initListItems(
                activity,
                R.id.characters,
                LinearLayoutManager.VERTICAL,
                new RecyclerViewUtil.RecyclerViewOnItemClick(
                        activity,
                        new RecyclerViewUtil.RecyclerViewOnItemClick.OnItemClickListener() {
                            @Override
                            public void onItemClick(View view, int position) {
                                Intent intent = new Intent(activity, ActivityCharacter.class);
                                intent.putExtra(Constants.ActivityCommunication.ACTCOMM_CHARACTER, CharacterUIService.this.mAdapter.getCharacter(position));
                                activity.startActivity(intent);
                            }
                        }
                ),
                this.mAdapter
        );

        NotificationCenter.RegistrationCenter.registerForEvent(EventCatalog.e0000, event0000Handler);
    }

    public void loadDescription(final Character character, AppCompatActivity activity) {
        this.mResources.getPicasso()
                .load(character.getThumbnail().getUrl())
                .fit()
                .centerCrop()
                .into(((ImageView) activity.findViewById(R.id.cover)));

        TextView name = (TextView) activity.findViewById(R.id.name);
        name.setText((character.getName() != null || !"".equals(character.getName()))
                ? character.getName()
                : activity.getString(R.string.rsc_util_unknown_value));

        TextView description = (TextView) activity.findViewById(R.id.character_description);
        description.setText((character.getDescription() != null && !"".equals(character.getDescription()))
                ? character.getDescription()
                : activity.getString(R.string.rsc_collections_description_not_found));
    }

    public void loadRelatedLinks(final List<Link> urls, final AppCompatActivity activity) {
        ListView links = (ListView) activity.findViewById(R.id.links);

        String[] values = {
                "DETAILS",
                "WIKI",
                "COMICLINK"
        };

        ArrayAdapter<String> adapter = new ArrayAdapter<>(
                activity,
                R.layout.rsc_character_related_links_item,
                R.id.name,
                values
        );

        links.setAdapter(adapter);
        mRecyclerViewUtil.fixListViewSize((int) activity.getResources().getDimension(R.dimen.rsc_collections_related_links_height), links);

        links.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                try {
                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urls.get(position).getUrl()));
                    activity.startActivity(browserIntent);
                } catch (ArrayIndexOutOfBoundsException e) {
                    Log.e(TAG, e.getMessage());
                }
            }
        });
    }

    public void loadCollection(int resourceID, final List<Item> dataset, final AppCompatActivity activity) {
        if (dataset.size() == 0) {
            this.showNotFoundMessage(resourceID, activity);
        } else {
            final AdapterItem adapter = new AdapterItem(dataset, LayoutType.SMALL_VERTICAL);
            mRecyclerViewUtil.initListItems(
                    activity,
                    resourceID,
                    LinearLayoutManager.HORIZONTAL,
                    new RecyclerViewUtil.RecyclerViewOnItemClick(
                            activity,
                            new RecyclerViewUtil.RecyclerViewOnItemClick.OnItemClickListener() {
                                @Override
                                public void onItemClick(View view, int position) {
                                    Intent intent = new Intent(activity, ActivityItem.class);
                                    intent.putExtra(Constants.ActivityCommunication.ACTCOMM_COLLECTIONS, (ArrayList<Item>) adapter.getDataset());
                                    intent.putExtra(Constants.ActivityCommunication.ACTCOMM_COLLECTIONS_STARTPOSITION, position);
                                    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                                            activity,
                                            adapter.getThumbnailView(position),
                                            "picture");
                                    activity.startActivity(intent, options.toBundle());
                                }
                            }
                    ),
                    adapter
            );
        }
    }

    private void showNotFoundMessage(int resourceID, AppCompatActivity activity) {
        switch (resourceID) {
            case R.id.comics:
                activity.findViewById(R.id.comic_not_found).setVisibility(TextView.VISIBLE);
                activity.findViewById(R.id.comics).setVisibility(TextView.GONE);
                break;
            case R.id.series:
                activity.findViewById(R.id.series_not_found).setVisibility(TextView.VISIBLE);
                activity.findViewById(R.id.series).setVisibility(TextView.GONE);
                break;
            case R.id.stories:
                activity.findViewById(R.id.stories_not_found).setVisibility(TextView.VISIBLE);
                activity.findViewById(R.id.stories).setVisibility(TextView.GONE);
                break;
            case R.id.events:
                activity.findViewById(R.id.events_not_found).setVisibility(TextView.VISIBLE);
                activity.findViewById(R.id.events).setVisibility(TextView.GONE);
                break;
        }
    }
} public class AdapterCharacter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Publisher {

    private static final String TAG = "AdapterCharacter";

    private List<Character> mDataset;
    private List<Subscriber> mSubscribers = new ArrayList<>();
    private LayoutType mViewType;

    @Inject
    App mApplication;
    @Inject
    StaticResourceProvider resourceManager;

    // CONSTRUCTOR
    public AdapterCharacter(List<Character> dataset, LayoutType viewType) {
        try {
            App.getComponent().inject(this);
        }catch (Exception e) {}
        this.mDataset = dataset;
        this.mViewType = viewType;
    }

    // CALLBACKS
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        View view;

        switch (LayoutType.fromInteger(viewType)) {
            case SMALL_HORIZONTAL:
                view = inflater.inflate(R.layout.rsc_util_character_small, parent, false);
                break;
            case LARGE_VERTICAL:
                view = inflater.inflate(R.layout.rsc_util_character_large, parent, false);
                break;
            default:
                view = inflater.inflate(R.layout.rsc_util_character_small, parent, false);
        }

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        this.notifyEvent(position);

        String name = this.mDataset.get(position).getName();

        final ViewHolder viewHolder = (ViewHolder) holder;
        viewHolder.name.setText(name);

        final File image = new File(
                Environment.getExternalStorageDirectory()
                        .getAbsolutePath()
                        .concat(Constants.MEDIA_DIRECTORY.concat(Constants.MEDIA_CHARACTER).concat(name.concat(Constants.MEDIA_EXTENSION)))
        );

        if (image.exists()) {
            this.resourceManager.getPicasso()
                    .load(image)
                    .fit()
                    .centerCrop()
                    .into(viewHolder.cover);
        } else {
            this.resourceManager.getPicasso()
                    .load(this.mDataset.get(position).getThumbnail().getUrl())
                    .fit()
                    .centerCrop()
                    .into(viewHolder.cover, new ImageWarehouse(name, viewHolder.cover, Constants.MEDIA_CHARACTER));
        }

        this.setAnimation(viewHolder.cover);
    }


    public void notifyCharacterRangeInserted(int range) {
        try {
            int start = this.getItemCount() - range;
            this.notifyItemRangeInserted(start, range);
        } catch (ArrayIndexOutOfBoundsException e) {
            Log.e(TAG, "Failed animate character insertion");
        }
    }

    private void setAnimation(View viewToAnimate) {
        Animation animation = AnimationUtils.loadAnimation(this.mApplication.getContext(), android.R.anim.slide_in_left);
        animation.setDuration(1000);
        viewToAnimate.startAnimation(animation);
    }

    // GETTERS AND SETTERS
    @Override
    public int getItemViewType(int position) {
        return this.mViewType.toInteger();
    }

    @Override
    public int getItemCount() {
        return this.mDataset.size();
    }

    public Character getCharacter(int pos) {
        return this.mDataset.get(pos);
    }

    public void setDataset(List<Character> dataSet) {
        this.mDataset = dataSet;
    }

    public void setCharacter(List<Character> dataSet) {
        this.mDataset.addAll(dataSet);
    }

    // PUBLISHER INTERFACE
    @Override
    public void registerSubscriber(Subscriber subscriber) {
        this.mSubscribers.add(subscriber);
    }

    @Override
    public void removeSubscriber(Subscriber subscriber) {
        this.mSubscribers.remove(subscriber);
    }

    @Override
    public void notifyEvent(Object... params) {
        for (Subscriber curr : this.mSubscribers) {
            curr.onEventListener(params);
        }
    }

    // INNER CLASSES
    public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView name;
        private ImageView cover;

        public ViewHolder(View v) {
            super(v);
            this.name = (TextView) v.findViewById(R.id.name);
            this.cover = (ImageView) v.findViewById(R.id.cover);
        }
    }
} public class AdapterItem extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Publisher {

    private static final String TAG = "AdapterItem";
    private static final int MAX_THREADS = 10;
    private static int THREAD_NUMBER = 0;

    private List<Item> mDataSet;

    private List<ViewHolder.ItemViewHolder> mHolders = new ArrayList<>();
    private Map<String, ViewHolder.ItemViewHolder> mURIViewHolder;
    private LayoutType mViewType;
    @Inject
    ViewHolderFactory mViewHolderFactory;
    @Inject
    StaticResourceProvider resourceManager;
    @Inject
    App mApplication;
    @Inject
    Postman mPostman;

    private List<Subscriber> mSubscribers = new ArrayList<>();

    // HANDLERS
    public TaskExecutor event0001Handler = new TaskExecutor() {
        @Override
        public void executeOnSuccessTask(Object response) {
            AdapterItem.THREAD_NUMBER--;
            try {
                String resourceUri = MarvelRequestParser.getResourceUri((JSONObject) response);
                String thumbnailUrl = MarvelRequestParser.getItemThumbnailUrl((JSONObject) response);
                String name = MarvelRequestParser.getTitle((JSONObject) response);

                for (String key : AdapterItem.this.mURIViewHolder.keySet()) {
                    ViewHolder.ItemViewHolder viewHolder = AdapterItem.this.mURIViewHolder.get(key);
                    if (resourceUri.equals(viewHolder.getResourceURI())) {
                        AdapterItem.this.mDataSet.get(viewHolder.getDatasetPosition()).setThumbnailUrl(thumbnailUrl);
                        AdapterItem.this.resourceManager.getPicasso()
                                .load(thumbnailUrl)
                                .placeholder(R.drawable.rsc_item_placeholder)
                                .fit()
                                .centerCrop()
                                .into(viewHolder.getThumbnail(), new ImageWarehouse(name, viewHolder.getThumbnail(), Constants.MEDIA_COLLECTIONS));
                    }
                }

            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }

        @Override
        public void executeOnErrorTask(Object payload) {
            AdapterItem.THREAD_NUMBER--;
            if (payload != null) {
                Log.e(TAG, payload.toString());
            }
        }
    };

    // CONSTRUCTOR
    @Inject
    public AdapterItem(List<Item> dataset, LayoutType viewType) {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
        }
        this.mDataSet = dataset;
        this.mViewType = viewType;
        this.mURIViewHolder = new HashMap<>();
        NotificationCenter.RegistrationCenter.registerForEvent(EventCatalog.e0001, this.event0001Handler);
    }

    // CALLBACKS
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return this.mViewHolderFactory.getViewHolder(parent, viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        this.notifyEvent(position);

        Item item = this.mDataSet.get(position);

        ViewHolder.ItemViewHolder viewHolder = (ViewHolder.ItemViewHolder) holder;
        viewHolder.getName().setText(item.getName());
        viewHolder.setResourceURI(item.getResourceURI());
        viewHolder.setDatasetPosition(position);
        this.mURIViewHolder.put(viewHolder.getResourceURI(), viewHolder);


        this.loadImage(
                item.getThumbnailUrl(),
                item.getName(),
                item.getResourceURI(),
                viewHolder.getThumbnail()
        );

        this.setAnimation(viewHolder.getThumbnail());

        if (!this.mHolders.contains(viewHolder)) {
            mHolders.add(viewHolder);
        }
    }

    private void loadImage(String url, String name, String uri, ImageView container) {
        File image;

        try {
            image = new File(
                    Environment.getExternalStorageDirectory()
                            .getAbsolutePath()
                            .concat(Constants.MEDIA_DIRECTORY.concat(Constants.MEDIA_COLLECTIONS).concat(name.concat(Constants.MEDIA_EXTENSION)))
            );
        } catch (Exception e) {
            image = null;
        }

        if (null != image && image.exists()) {
            this.resourceManager.getPicasso()
                    .load(image)
                    .fit()
                    .centerInside()
                    .placeholder(R.drawable.rsc_item_placeholder)
                    .into(container);
        } else {
            if (null == url || "".equals(url) && AdapterItem.THREAD_NUMBER < AdapterItem.MAX_THREADS) {
                this.resourceManager.getPicasso()
                        .load(R.drawable.rsc_item_placeholder)
                        .fit()
                        .centerInside()
                        .into(container);

                AdapterItem.THREAD_NUMBER++;
                this.mPostman.listComicDetails(uri, this.mApplication.getContext());
            }
        }
    }

    private void setAnimation(View viewToAnimate) {
        Animation animation = AnimationUtils.loadAnimation(this.mApplication.getContext(), android.R.anim.fade_in);
        animation.setDuration(1000);
        viewToAnimate.startAnimation(animation);
    }

    // GETTERS AND SETTERS
    @Override
    public int getItemViewType(int position) {
        return this.mViewType.toInteger();
    }

    @Override
    public int getItemCount() {
        return this.mDataSet.size();
    }

    public List<Item> getDataset() {
        return mDataSet;
    }

    public View getThumbnailView(int pos) {
        return this.mURIViewHolder.get(this.mDataSet.get(pos).getResourceURI()).getThumbnail();
    }

    // PUBLISHER INTERFACE
    @Override
    public void registerSubscriber(Subscriber subscriber) {
        this.mSubscribers.add(subscriber);
    }

    @Override
    public void removeSubscriber(Subscriber subscriber) {
        this.mSubscribers.remove(subscriber);
    }

    @Override
    public void notifyEvent(Object... params) {
        for (Subscriber curr : this.mSubscribers) {
            curr.onEventListener(params);
        }
    }
} public class StaticResourceProvider {

    @Inject
    App mApplication;

    public StaticResourceProvider() {
        App.getComponent().inject(this);
    }

    public Picasso getPicasso() {
        return Picasso.with(this.mApplication.getContext());
    }

    public MaterialDialog.Builder getDialogBuilder(Context context) {
        return new MaterialDialog.Builder(context);
    }
} public class ActivityCharacter extends ActivityTemplate {

    private static final String TAG = "ActivityCharacter";

    @Inject
    CharacterUIService characterUIService;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        this.overridePendingTransition(android.R.anim.slide_out_right, android.R.anim.slide_out_right);
    }

    @Override
    public ActivityBasicConfiguration setupActivity() {
        return ActivityFactory.getActivityConfiguration(this);
    }

    @Override
    public void startResources() {
        this.injectMembers();
        super.startResources();

        Character character = (
                this.retrieveElement(Constants.ActivityCommunication.ACTCOMM_CHARACTER) instanceof Character)
                ? (Character) this.retrieveElement(Constants.ActivityCommunication.ACTCOMM_CHARACTER)
                : null;

        this.characterUIService.loadDescription(character, this);
        this.characterUIService.loadRelatedLinks(character.getUrls(), this);
        this.characterUIService.loadCollection(R.id.comics, character.getComics().getItems(), this);
        this.characterUIService.loadCollection(R.id.series, character.getSeries().getItems(), this);
        this.characterUIService.loadCollection(R.id.stories, character.getStories().getItems(), this);
        this.characterUIService.loadCollection(R.id.events, character.getEvents().getItems(), this);
    }

    private void injectMembers () {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
            Log.e(TAG, "Could not inject members");
        }
    }
} public class ActivityHomepage extends ActivityTemplate {

    private static final String TAG = "ActivityHomepage";
    private static final int REQUEST_WRITE_EXTERNAL_PERMISSION = 1;

    public static Boolean LOADING = Boolean.FALSE;

    @Inject
    CharacterUIService characterUIService;
    @Inject
    PermissionsUtil mPermissionsManager;
    @Inject
    StaticResourceProvider mStaticResourceProvider;
    @Inject
    Postman mPostman;
    @Inject
    NotificationManager mNotificationManager;
    @Inject
    LoaderManager mLoaderManager;

    @Override
    public ActivityBasicConfiguration setupActivity() {
        return ActivityFactory.getActivityConfiguration(this);
    }

    @Override
    public void startResources() {
        this.injectMembers();
        super.startResources();
        mLoaderManager.showCircularProgressBar(this);
        if (this.mPermissionsManager.verifyStoragePermissions(this, REQUEST_WRITE_EXTERNAL_PERMISSION)) {
            this.mPostman.listCharacters(
                    this,
                    "",
                    this.getResources().getInteger(R.integer.MARVEL_API_PARAM_DEFAULT_LIMIT),
                    this.getResources().getInteger(R.integer.MARVEL_API_PARAM_DEFAULT_OFFSET)
            );
        }
        this.initDrawerLayout();
        ActivityHomepage.this.characterUIService.loadCharacters(this);
    }

    private void injectMembers() {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
            Log.e(TAG, "Could not inject members");
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_WRITE_EXTERNAL_PERMISSION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    this.mPostman.listCharacters(
                            this,
                            "",
                            this.getResources().getInteger(R.integer.MARVEL_API_PARAM_DEFAULT_LIMIT),
                            this.getResources().getInteger(R.integer.MARVEL_API_PARAM_DEFAULT_OFFSET)
                    );
                } else {
                    mNotificationManager.showPopUp(this,
                            "We're sorry...",
                            "In order to use Marvel Challenge you need to allow the app to write in the external storage of your phone",
                            R.color.colorPrimaryDark
                    );
                }
            }
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_search:
                Intent intent = new Intent(ActivityHomepage.this, ActivitySearch.class);
                this.startActivity(intent);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    public void initDrawerLayout() {
        final DrawerLayout draweLayout = (DrawerLayout) this.findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(
                this,
                draweLayout,
                ActivityHomepage.this.getToolbarService().getToolbar(),
                R.string.drawer_open,
                R.string.drawer_close
        )

        {
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                ActivityHomepage.this.invalidateOptionsMenu();
                syncState();
            }

            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                ActivityHomepage.this.invalidateOptionsMenu();
                syncState();
            }
        };

        draweLayout.setDrawerListener(actionBarDrawerToggle);
        actionBarDrawerToggle.syncState();
        this.addAboutLibrariesActivity();
    }

    private void addAboutLibrariesActivity() {
        ButtonRectangle button = (ButtonRectangle) this.findViewById(R.id.about_libraries);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new LibsBuilder()
                        .withActivityTitle(ActivityHomepage.this.getString(R.string.app_name))
                        .withAboutIconShown(true)
                        .withAboutVersionShown(true)
                        .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
                        .start(ActivityHomepage.this);
            }
        });
    }
} public class ActivityItem extends ActivityTemplate {

    private static final String TAG = "ActivityItem";

    private PagerAdapter mPagerAdapter;
    private ViewPager mPager;
    private List<Item> mItems;
    private int mStartingPosition;

    public ActivityBasicConfiguration setupActivity() {
        return ActivityFactory.getActivityConfiguration(this);
    }

    @Override
    public void startResources() {
        this.injectMembers();
        super.startResources();
        this.loadItems();
    }

    private void injectMembers () {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
            Log.e(TAG, "Could not inject members");
        }
    }

    private void loadItems () {
        this.mPager = (ViewPager) findViewById(R.id.items);
        this.mItems = this.getItems();
        this.mStartingPosition = this.getStartPosition();
        this.mPagerAdapter = new ScreenSlidePagerAdapter(this.getSupportFragmentManager());
        this.mPager.setAdapter(mPagerAdapter);
        this.mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                ActivityItem.this.updateItemPosition(position);
            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
        mPager.setPageTransformer(true, new DepthPageTransformer());
        this.mPager.setCurrentItem(this.mStartingPosition);
    }

    public String updateItemPosition(int position) {
        TextView title = (TextView) this.getToolbarService().getToolbar().findViewById(R.id.title);
        String label = new Integer(position + 1).toString().concat(" / ").concat(new Integer(this.mItems.size()).toString());
        title.setText(label);
        return label;
    }

    @Override
    public void onBackPressed() {
        if ((this.mPager.getCurrentItem() == this.mStartingPosition)) {
            super.onBackPressed();
        } else if (this.mPager.getCurrentItem() < this.mStartingPosition) {
            this.mPager.setCurrentItem(mPager.getCurrentItem() + 1);
        } else {
            this.mPager.setCurrentItem(mPager.getCurrentItem() - 1);
        }
    }

    private int getStartPosition () {
        return this
                .getIntent()
                .getExtras()
                .getInt(Constants.ActivityCommunication.ACTCOMM_COLLECTIONS_STARTPOSITION);
    }

    private List<Item> getItems() {
        Bundle extras = this.getIntent().getExtras();
        Object object = extras.getSerializable(Constants.ActivityCommunication.ACTCOMM_COLLECTIONS);
        return (object instanceof ArrayList) ? (ArrayList<Item>) object : null;
    }

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            Bundle bundle = new Bundle();
            bundle.putSerializable(Constants.ActivityCommunication.ACTCOMM_COLLECTIONS, mItems.get(mPager.getCurrentItem()));
            FragmentItem obj = new FragmentItem();
            obj.setArguments(bundle);
            return obj;
        }

        @Override
        public int getCount() {
            return ActivityItem.this.mItems.size();
        }
    }
} public class ActivitySearch extends ActivityTemplate {

    private static final String TAG = "ActivitySearch";

    @Inject
    CharacterSearchService characterSearchService;

    @Override
    public ActivityBasicConfiguration setupActivity() {
        return ActivityFactory.getActivityConfiguration(this);
    }

    @Override
    public void startResources() {
        this.injectMembers();
        super.startResources();
        this.characterSearchService.initSearchInterface(this);
    }

    private void injectMembers () {
        try {
            App.getComponent().inject(this);
        } catch (Exception e) {
            Log.e(TAG, "Could not inject members");
        }
    }
} public class FragmentItem extends Fragment {

    private static String TAG = "FragmentItem";

    private ImageView mContainer;
    private ViewGroup mRootView;
    @Inject
    App mApplication;
    @Inject
    StaticResourceProvider resourceManager;
    @Inject
    Postman mPostman;

    public TaskExecutor event0001Handler = new TaskExecutor() {
        @Override
        public void executeOnSuccessTask(Object response) {
            try {
                String thumbnailUrl = MarvelRequestParser.getItemThumbnailUrl((JSONObject) response);
                String name = MarvelRequestParser.getTitle((JSONObject) response);

                FragmentItem.this.mRootView.findViewById(R.id.name).setVisibility(TextView.VISIBLE);
                FragmentItem.this.resourceManager.getPicasso()
                        .load(thumbnailUrl)
                        .fit()
                        .centerInside()
                        .into(FragmentItem.this.mContainer, new ImageWarehouse(name, FragmentItem.this.mContainer, Constants.MEDIA_COLLECTIONS));
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }
        @Override
        public void executeOnErrorTask(Object payload) {
            if (payload != null) {
                Log.e(TAG, payload.toString());
            }
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {
        App.getComponent().inject(this);
        NotificationCenter.RegistrationCenter.registerForEvent(EventCatalog.e0001, this.event0001Handler);

        this.mRootView = (ViewGroup) inflater.inflate(R.layout.rsc_util_item_large, container, false);
        this.mContainer = (ImageView) this.mRootView.findViewById(R.id.thumbnail);
        Item item = (Item) getArguments().getSerializable(Constants.ActivityCommunication.ACTCOMM_COLLECTIONS);

        TextView name = (TextView) this.mRootView.findViewById(R.id.name);
        name.setText(item.getName());
        name.setVisibility(TextView.VISIBLE);

        this.loadImage(item.getResourceURI(), item.getName());

        return this.mRootView;
    }

    private void loadImage(String uri, String name) {
        File image = new File(
                Environment.getExternalStorageDirectory()
                        .getAbsolutePath()
                        .concat(Constants.MEDIA_DIRECTORY.concat(Constants.MEDIA_COLLECTIONS).concat(name.concat(Constants.MEDIA_EXTENSION)))
        );
        if (image.exists()) {
            FragmentItem.this.resourceManager.getPicasso()
                    .load(image)
                    .fit()
                    .centerInside()
                    .into((ImageView) this.mRootView.findViewById(R.id.thumbnail));
        } else {
            this.mPostman.listComicDetails(uri, this.mApplication.getContext());
        }
    }

} @Module
public class ModuleBusiness {

    @Provides
    @Singleton
    public CharacterUIService provideCharacterService() {
        return new CharacterUIService();
    }

    @Provides
    @Singleton
    public CharacterSearchService provideCharacterSearchInterface() {
        return new CharacterSearchService();
    }

    @Provides
    @Singleton
    public ViewHolderFactory provideViewHolderFactor() {
        return new ViewHolderFactory();
    }
} @Module
public class ModuleUtil {

    private final Context context;

    public ModuleUtil(Context context) {
        this.context = context;
    }

    @Provides
    public ToolbarManager provideToolbarManager() {
        return new ToolbarManager();
    }

    @Provides
    @Singleton
    public Postman providePostman() {
        return new Postman();
    }

    @Provides
    @Singleton
    public PermissionsUtil providePermissionUtil() {
        return new PermissionsUtil();
    }

    @Singleton
    @Provides
    public App provideApplication() {
        return new App();
    }

    @Provides
    public Picasso providePicasso(Context context) {
        return Picasso.with(context);
    }

    @Singleton
    @Provides
    public StaticResourceProvider provideResourceManager() {
        return new StaticResourceProvider();
    }

    @Provides
    @Singleton
    public Context provideContext() {
        return context;
    }

    @Provides
    @Singleton
    public RecyclerViewUtil provideRecyclerViewUtil() {
        return new RecyclerViewUtil();
    }

    @Provides
    @Singleton
    public NotificationManager provideNotificationManager() {
        return new NotificationManager();
    }

    @Provides
    @Singleton
    public LoaderManager provideLoaderManager() {
        return new LoaderManager();
    }

}
import javax.inject.Singleton;
import br.com.hole19.marvel.ui.bpo.CharacterSearchService;
import br.com.hole19.marvel.ui.bpo.CharacterUIService;
import br.com.hole19.marvel.ui.commons.adapter.AdapterCharacter;
import br.com.hole19.marvel.ui.commons.adapter.AdapterItem;
import br.com.hole19.marvel.ui.commons.util.StaticResourceProvider;
import br.com.hole19.marvel.ui.controller.ActivityCharacter;
import br.com.hole19.marvel.ui.controller.ActivityHomepage;
import br.com.hole19.marvel.ui.controller.ActivityItem;
import br.com.hole19.marvel.ui.controller.ActivitySearch;
import br.com.hole19.marvel.ui.controller.FragmentItem;
import br.com.hole19.marvel.ui.infrastructure.modules.ModuleBusiness;
import br.com.hole19.marvel.ui.infrastructure.modules.ModuleUtil;
import dagger.Component;
package br.com.hole19.marvel.ui.infrastructure;



/**
 * Created by edgar on 04-May-16.
 */
@Singleton
@Component(
        modules = {
                ModuleBusiness.class,
                ModuleUtil.class
        })
public interface MainComponent {
    void inject(ActivityHomepage target);
    void inject(AdapterCharacter target);
    void inject(StaticResourceProvider target);
    void inject(CharacterUIService target);