public class PostActivity extends AbstractAppActivity implements PostView {

    @Inject PostsApplication application;

    @Inject PostPresenter postPresenter;

    @BindView(R.id.activity_post__coordinator_layout) CoordinatorLayout coordinatorLayout;
    @BindView(R.id.activity_post__toolbar) Toolbar toolbar;

    @BindView(R.id.activity_post__avatar_circle_image_view) CircleImageView circleImageView;
    @BindView(R.id.activity_post__name_text_view) AppCompatTextView nameTextView;
    @BindView(R.id.activity_post__username_text_view) AppCompatTextView usernameTextView;
    @BindView(R.id.activity_post__title_text_view) AppCompatTextView titleTextView;
    @BindView(R.id.activity_post__body_text_view) AppCompatTextView bodyTextView;
    @BindView(R.id.activity_post__number_of_comments_text_view) AppCompatTextView numberOfCommentsTextView;

    // LIFECYCLE // *********************************************************************************************************

    @Override
    @SuppressFBWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE")
    protected void onCreate(@Nullable final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post);
        setActivityDependencies();
        setToolbar(toolbar);
        setSnackbar(coordinatorLayout);
        setPresenter();
        loadPostUserAndPost();
    }

    protected void setActivityDependencies() {
        PostsApplication.getApplicationComponent().inject(this);
        ButterKnife.bind(this);
    }

    private void setPresenter() {
        postPresenter.attachView(this);
    }

    @Override
    public void loadPostUserAndPost() {
        postPresenter.loadPostUserAndPost(getIntent().getExtras());
    }

    @Override
    protected void onResume() {
        super.onResume();
        setSnackbar(coordinatorLayout);
    }

    @Override
    protected void onDestroy() {
        unsetPresenter();
        super.onDestroy();
    }

    private void unsetPresenter() {
        postPresenter.detachView();
    }

    // VIEW // **************************************************************************************************************

    @Override
    public void loadPostUser(final String userEmail, final String userName, final String userUsername) {
        Picasso.with(getApplicationContext())
                .load(AvatarUtilities.getUri(userEmail))
                .placeholder(R.mipmap.ic_launcher)
                .error(R.mipmap.ic_launcher)
                .into(circleImageView);
        nameTextView.setText(userName);
        usernameTextView.setText(userUsername);
    }

    @Override
    public void notifyUserAboutUserUnavailability() {
        application.snackbar().warn(R.string.snackbar__text__cannot_retrieve_user);
    }

    @Override
    public void notifyUserAboutInternetUnavailability() {
        application.snackbar().warn(R.string.snackbar__text__internet_not_available);
        numberOfCommentsTextView.setText(R.string.activity__not_applicable);
    }

    @Override
    public void loadPost(final String postTitle, final String postBody, final String numberOfComments) {
        titleTextView.setText(postTitle);
        bodyTextView.setText(postBody);
        numberOfCommentsTextView.setText(numberOfComments);
    }

    @Override
    public void notifyUserAboutPostUnavailability() {
        application.snackbar().warn(R.string.snackbar__text__cannot_retrieve_post);
    }

    @Override
    public void showError() {
        application.snackbar().warn(R.string.snackbar__text__error_getting_comments);
    }

    // MENU ITEMS // ********************************************************************************************************

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        final int itemId = item.getItemId();
        if (itemId == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

} public class PostsActivity extends AbstractAppActivity {

    @Inject PostsApplication application;

    @BindView(R.id.activity_posts__coordinator_layout) CoordinatorLayout coordinatorLayout;
    @BindView(R.id.activity_posts__toolbar) Toolbar toolbar;

    // LIFECYCLE // *********************************************************************************************************

    @Override
    @SuppressFBWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE")
    protected void onCreate(@Nullable final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_posts);
        setActivityDependencies();
        setSupportActionBar(toolbar);
        setSnackbar(coordinatorLayout);
        setFragment(R.id.activity_posts__fragment);
    }

    protected void setActivityDependencies() {
        PostsApplication.getApplicationComponent().inject(this);
        ButterKnife.bind(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        setSnackbar(coordinatorLayout);
    }

    // ON CLICK // **********************************************************************************************************

    @OnClick(R.id.activity_posts__fab)
    public void floatingActionButtonClicked() {
        application.snackbar().info(R.string.snackbar__text__pull_down_to_refresh);
    }

} public class PostsFragment extends MvpLceFragment<SwipeRefreshLayout, List<Post>, PostsView, PostsPresenter>
        implements PostsView, SwipeRefreshLayout.OnRefreshListener {

    @Inject PostsPresenter postsPresenter;

    @BindString(R.string.dialog__loading__message) String loadingMessage;

    @BindView(R.id.posts_fragment__posts_recycler_view) RecyclerView postsRecyclerView;
    @VisibleForTesting PostRecyclerViewAdapter postRecyclerViewAdapter;

    @VisibleForTesting ProgressDialog progressDialog;

    // LIFECYCLE // *********************************************************************************************************

    @Override
    public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
                             @Nullable final Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_posts, container, false);
        PostsApplication.getApplicationComponent().inject(this);
        ButterKnife.bind(this, view);
        return view;
    }

    @Override
    public void onViewCreated(final View view, @Nullable final Bundle savedInstance) {
        super.onViewCreated(view, savedInstance);
        setSwipeRefreshView();
        setRecyclerView();
        loadData(false);
    }

    private void setSwipeRefreshView() {
        contentView.setOnRefreshListener(this);
    }

    private void setRecyclerView() {
        final OnPostViewClickListener onPostClickListenerImpl = new OnPostViewClickListener(getActivity());
        postRecyclerViewAdapter = new PostRecyclerViewAdapter(onPostClickListenerImpl);
        postsRecyclerView.setAdapter(postRecyclerViewAdapter);
    }

    @Override
    public void loadData(final boolean pullToRefresh) {
        presenter.loadPosts(pullToRefresh);
    }

    @Override
    public PostsPresenter createPresenter() {
        return postsPresenter;
    }

    @Override
    public void onDestroyView() {
        postsPresenter.detachView(false);
        super.onDestroyView();
    }

    // VIEW // **************************************************************************************************************

    @Override
    public void notifyUserAboutUsersUnavailability() {
        PostsApplication.snackbar(getActivity()).warn(R.string.snackbar__text__cannot_save_users);
    }

    @Override
    public void notifyUserAboutInternetUnavailability() {
        contentView.setRefreshing(false);
        PostsApplication.snackbar(getActivity()).warn(R.string.snackbar__text__internet_not_available);
    }

    @Override
    public void showLoading(final boolean pullToRefresh) {
        super.showLoading(pullToRefresh);
        contentView.setRefreshing(pullToRefresh);
        progressDialog = new ProgressDialog(getContext());
        progressDialog.setTitle(R.string.dialog__loading__title);
        progressDialog.setMessage(loadingMessage);
        progressDialog.show();
    }

    @Override
    public void setData(final List<Post> posts) {
        postRecyclerViewAdapter.reloadAdapter(posts);
    }

    @Override
    public void showContent() {
        super.showContent();
        contentView.setRefreshing(false);
        progressDialog.dismiss();
    }

    @Override
    public void showError(final Throwable throwable, final boolean pullToRefresh) {
        super.showError(throwable, pullToRefresh);
        contentView.setRefreshing(pullToRefresh);
        progressDialog.dismiss();
        PostsApplication.snackbar(getActivity()).warn(R.string.snackbar__text__error_getting_posts);
    }

    @Override
    protected String getErrorMessage(final Throwable throwable, final boolean pullToRefresh) {
        return "Error while loading posts!";
    }

    // SWIPE REFRESH // *****************************************************************************************************

    @Override
    public void onRefresh() {
        loadData(true);
    }

} public class PostViewHolder extends RecyclerView.ViewHolder {

    @Inject Datastore datastore;

    @BindView(R.id.item_view_post__avatar_circle_image_view) CircleImageView circleImageView;
    @BindView(R.id.item_view_post__title_text_view) AppCompatTextView titleTextView;

    PostViewHolder(final View itemView) {
        super(itemView);
        PostsApplication.getApplicationComponent().inject(this);
        ButterKnife.bind(this, itemView);
    }

    void bind(final Post post, final OnViewClickListener onViewClickListener) {
        setOnPostClickListener(post, onViewClickListener);
        setViews(post);
    }

    private void setOnPostClickListener(final Post post, final OnViewClickListener onViewClickListener) {
        itemView.setOnClickListener(view -> onViewClickListener.onViewClick(circleImageView, post));
    }

    private void setViews(final Post post) {
        setCircleImageView(post);
        setTitleTextView(post);
    }

    private void setCircleImageView(final Post post) {
        @Nullable final User user = datastore.get().user(post.getUserId());
        if (user != null) {
            Picasso.with(itemView.getContext())
                    .load(AvatarUtilities.getUri(user.getEmail()))
                    .placeholder(R.mipmap.ic_launcher)
                    .error(R.mipmap.ic_launcher)
                    .into(circleImageView);
        } else {
            Timber.e("Cannot get a user for this post!");
        }
    }

    private void setTitleTextView(final Post post) {
        titleTextView.setText(post.getTitle());
    }

} @SuppressWarnings("checkstyle:overloadmethodsdeclarationorder")
public class PostsApplication extends Application {

    private static ApplicationComponent applicationComponent;

    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); // NOTE: This enables the "proxy" trick on the vector images.
    }

    @Inject AppSnackbarActions appSnackbarActions;

    // HELPER // ************************************************************************************************************

    public static ApplicationComponent getApplicationComponent() {
        return applicationComponent;
    }

    public static SnackbarActions snackbar(final Activity activity) {
        return ((PostsApplication) activity.getApplication()).snackbar();
    }

    // APPLICATION // *******************************************************************************************************

    @Override
    public void onCreate() {
        super.onCreate();
        initDagger();
        initRealm();
        initTimber();
        Timber.i("Posts application created!");
    }

    private void initDagger() {
        applicationComponent = DaggerApplicationComponent.builder()
                .appModule(new AppModule(this))
                .netModule(new NetModule(BASE_URL))
                .dataModule(new DataModule())
                .serviceModule(new ServiceModule())
                .activityModule(new ActivityModule()).build();
        applicationComponent.inject(this);
    }

    private void initTimber() {
        Timber.plant(new Timber.DebugTree());
    }

    protected void initRealm() {
        Realm.init(this);
    }

    // GET // ***************************************************************************************************************

    public Realm getDefaultRealmInstance() {
        return Realm.getDefaultInstance();
    }

    // ACTIONS // ***********************************************************************************************************

    public SnackbarActions snackbar() {
        return appSnackbarActions;
    }

} @Module
public class ActivityModule {

    @Provides
    @Singleton
    PostsModel providesPostsModel(final Datastore datastore) {
        return new PostsModelImpl(datastore);
    }

    @Provides
    @Singleton
    PostsPresenter providesPostsPresenter(final PostsModel postsModel, final RetrofitService retrofitService,
                                          final RxSchedulers rxSchedulers,
                                          final InternetAvailabilityDetector internetAvailabilityDetector) {
        return new PostsPresenterImpl(postsModel, retrofitService, rxSchedulers, internetAvailabilityDetector);
    }

    @Provides
    @Singleton
    PostModel providesPostModel(final Datastore datastore) {
        return new PostModelImpl(datastore);
    }

    @Provides
    @Singleton
    PostPresenter providesPostPresenter(final PostModel postModel, final RetrofitService retrofitService,
                                        final RxSchedulers rxSchedulers,
                                        final InternetAvailabilityDetector internetAvailabilityDetector) {
        return new PostPresenterImpl(postModel, retrofitService, rxSchedulers, internetAvailabilityDetector);
    }

} @Module
public class AppModule {

    private final PostsApplication application;

    public AppModule(final PostsApplication application) {
        this.application = application;
    }

    @Provides
    @Singleton
    PostsApplication providesPostsApplication() {
        return application;
    }

    @Provides
    @Singleton
    AppSnackbarActions providesAppSnackbarActions() {
        return new AppSnackbarActions(application);
    }

} @Module
public class DataModule {

    @Provides
    @Singleton
    Realm provideRealm(final PostsApplication application) {
        return application.getDefaultRealmInstance();
    }

    @Provides
    @Singleton
    Datastore provideDatastore(final DatastoreSaveActions datastoreSaveActions, final DatastoreAddActions datastoreAddActions,
                               final DatastoreGetActions datastoreGetActions, final DatastoreUpdateActions datastoreUpdateActions) {
        return new Datastore(datastoreSaveActions, datastoreAddActions, datastoreGetActions, datastoreUpdateActions);
    }

    @Provides
    @Singleton
    DatastoreSaveActions provideDatastoreSaveActions(final DatastoreAddActions datastoreAddActions,
                                                     final DatastoreGetActions datastoreGetActions,
                                                     final DatastoreUpdateActions datastoreUpdateActions) {
        return new DatastoreSaveActions(datastoreAddActions, datastoreGetActions, datastoreUpdateActions);
    }

    @Provides
    @Singleton
    DatastoreAddActions provideDatastoreAddActions(final Realm realm) {
        return new DatastoreAddActions(realm);
    }

    @Provides
    @Singleton
    DatastoreGetActions provideDatastoreGetActions(final Realm realm) {
        return new DatastoreGetActions(realm);
    }

    @Provides
    @Singleton
    DatastoreUpdateActions provideDatastoreUpdateActions(final Realm realm) {
        return new DatastoreUpdateActions(realm);
    }

} @Module
public class NetModule {

    private final String baseUrl;

    public NetModule(final String baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Provides
    @Singleton
    RxSchedulers providesRxSchedulers() {
        return new RxSchedulers(Schedulers.io(), Schedulers.computation(), Schedulers.trampoline(),
                AndroidSchedulers.mainThread());
    }

    @Provides
    @Singleton
    RetrofitService provideRetroService() {
        final Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();
        return retrofit.create(RetrofitService.class);
    }

} @Module
public class ServiceModule {

    @Provides
    @Singleton
    InternetAvailabilityDetector provideInternetAvailabilityDetector(final PostsApplication application) {
        return new InternetAvailabilityDetector(application);
    }

}
import javax.inject.Singleton;
import dagger.Component;
import io.petros.posts.activity.post.view.PostActivity;
import io.petros.posts.activity.posts.PostsActivity;
import io.petros.posts.activity.posts.view.PostsFragment;
import io.petros.posts.activity.posts.view.recycler.PostViewHolder;
import io.petros.posts.app.PostsApplication;
import io.petros.posts.app.graph.modules.ActivityModule;
import io.petros.posts.app.graph.modules.AppModule;
import io.petros.posts.app.graph.modules.DataModule;
import io.petros.posts.app.graph.modules.NetModule;
import io.petros.posts.app.graph.modules.ServiceModule;
package io.petros.posts.app.graph.components;



@Singleton
@Component(modules = {AppModule.class, NetModule.class, DataModule.class, ServiceModule.class, ActivityModule.class})
public interface ApplicationComponent {

    // APPLICATION // *******************************************************************************************************

    void inject(final PostsApplication application);

    // POSTS ACTIVITY // ****************************************************************************************************

    void inject(final PostsActivity postsActivity);

    void inject(final PostsFragment postsFragment);

    void inject(final PostViewHolder postViewHolder);