Injecting presenter subclass to view android

I am creating an android application using the MVP pattern. For dependency injection I use dagger 2. I have an abstract fragment that implements the "view" interface, making it the view in Model-View-Presenter.

public abstract class MediaDetailFragment extends Fragment implements DetailsMvpView {
private static final String RESOURCE = "resource";
private static final String POSTER_SIZE = "w342/";


@Inject DetailPresenter mDetailPresenter;
@Inject CreditPresenter mCreditPresenter;
@BindView(R.id.media_image_flipper) ControllableFlipper mImageFlipper;
@BindView(R.id.keyword_recyclerview) RecyclerView mKeywordRecyclerView;
@BindView(R.id.title_textview) TextView mTitleTextView;
@BindView(R.id.overview_content_textview) TextView mOverviewTextView;
@BindView(R.id.cast_recyclerview) CreditRecyclerView mCastRecyclerView;
@BindView(R.id.crew_recyclerview) CreditRecyclerView mCrewRecyclerView;
@BindView(R.id.empty_view) TextView mRecyclerviewEmpty;
@BindView(R.id.button_share) ImageButton mShare;
@BindView(R.id.button_trailers) ImageButton mTrailers;
@BindView(R.id.button_reviews) ImageButton mReviews;
@BindBool(R.bool.isTablet) boolean mIsTablet;

@OnClick(R.id.button_share) public void shareMedia(View view) {
    startActivity(new Intent(Intent.ACTION_SEND)
            .putExtra(Intent.EXTRA_TEXT, getActivity().getString(R.string.base_youtube_url)
                    + POSTER_SIZE + mMedia.posterPath() + "\n\n"
                    + mMedia.title() + "\n\n" + mMedia.overview())
            .setType("text/plain"));
}

@OnClick(R.id.button_trailers) public void requestTrailers(View view) {
    mDetailPresenter.loadMovies(mMedia.id());
}

@OnClick(R.id.button_reviews) public void requestReviews(View view) {
    if (NetworkUtil.isNetworkConnected(getContext())) {
        ((FragmentHandler) getActivity()).onReviewsRequested(mMedia);
    } else {
        ViewUtil.displayNoNetworkSnackbar(getActivity());
    }
}

private Media mMedia;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private OnGlobalLayoutListener mListener;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mMedia = getArguments().getParcelable(RESOURCE);
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.media_details_fragment, container, false);

    ((BaseActivity) getActivity()).activityComponent().inject(this);
    ButterKnife.bind(this, rootView);

    getActivity().getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);

    mTitleTextView.setText(mMedia.title());
    mOverviewTextView.setText(mMedia.overview());

    mKeywordRecyclerView.setAdapter(new KeywordAdapter(getContext()));
    mCastRecyclerView.setAdapter(R.layout.item_credit_normal, mRecyclerviewEmpty);
    mCrewRecyclerView.setAdapter(R.layout.item_credit_normal, mRecyclerviewEmpty);

    mListener = () -> rootView.post(() -> {
        if (mIsTablet) {
            mImageFlipper.getLayoutParams().height =
                    ViewUtil.setHeightForAspectRatio(rootView.getWidth(), ViewUtil.STANDARD);
        } else {
            mImageFlipper.getLayoutParams().height =
                    ViewUtil.setHeightForAspectRatio(mDisplayMetrics.widthPixels, ViewUtil.STANDARD);
        }
        rootView.getViewTreeObserver().removeOnGlobalLayoutListener(mListener);
    });
    rootView.getViewTreeObserver().addOnGlobalLayoutListener(mListener);

    return rootView;
}

@Override
public void onStart() {
    super.onStart();

    mDetailPresenter.attachView(this);
    mDetailPresenter.loadImages(mMedia.id());
    mDetailPresenter.loadKeywords(mMedia.id());

    mCreditPresenter.attachCastView(mCastRecyclerView);
    mCreditPresenter.attachCrewView(mCrewRecyclerView);
    mCreditPresenter.loadCredits(mMedia.id());
}

@Override
public void onStop() {
    mDetailPresenter.detachView();
    mCreditPresenter.detachCastView();
    mCreditPresenter.detachCrewView();
    super.onStop();
}

@Override
public void showImages(String image) {
    mImageFlipper.addImagePath(image);
}

@Override
public void showKeywords(List<String> keywords) {
    ((KeywordAdapter) mKeywordRecyclerView.getAdapter()).setKeywords(keywords);
}

@Override
public void showVideos(List<Video> videos) {
    TrailerDialogFragment.newInstance(videos).show(getFragmentManager(), null);
}

@Override
public void showError() {
    if (!NetworkUtil.isNetworkConnected(getContext())) {
        ViewUtil.displayNoNetworkSnackbar(getActivity());
    }
}
}

Depending on the situation I will use one of two subclasses of this abstract view. As you can see, I inject a "DetailsPresenter" into the view. DetailsPresenter is also an abstract class with two subclasses (one for each subclass of the view).

However, if the view is a MovieDetailFragment, it should have a MovieDetailPresenter, and if it is a ShowDetailFragment, it should have a ShowDetailPresenter.

My question is: where should I implement the logic for checking what kind of view it is and providing the right DetailsPresenter? Should I do it in the Dagger module (maybe in the method that returns the presenter)? Should I do it in the fragment itself?

This is my module:

@Module
public class ConfigPersistentModule {

    @Provides
    DetailPresenter provideDetailPresenter(DataManager dataManager) {
        return new MovieDetailPresenter(dataManager);
    }
}

Please help me.

Upvotes: 1

Views: 293

Answers (1)

Amir Ziarati
Amir Ziarati

Reputation: 15087

You must inject your presenter in the child classes (MovieDetailFragment & ShowDetailFragment) and in your module you need to have TWO provide module like below :

@Module
public class ConfigPersistentModule {

@Provides
MovieDetailFragment provideDetailPresenter(DataManager dataManager) {
    return new MovieDetailPresenter(dataManager);
}

@Provides
ShowDetailFragment provideDetailPresenter(DataManager dataManager) {
    return new ShowDetailFragment(dataManager);
}
}

and then in your child classes inject like this :

in ShowDetailFragment:

@Inject ShowDetailFragment mCreditPresenter;

in MovieDetailFragment:

@Inject MovieDetailFragment mCreditPresenter;

now in your father class (MediaDetailFragment) put a abstract function called "getPresenter" that is mandatory for children to override it, like below:

public abstract class MediaDetailFragment extends Fragment implements DetailsMvpView {
   private mDetailPresenter;

   public abstract DetailPresenter getPresenter();

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
      this.mDetailPresenter = getPresenter();
      ...
      ...
   }
   ...
   ...
}

now every class implementing MediaDetailFragment have to tell the father about his presenter and father uses that presenter. so in the children classes (MovieDetailFragment & ShowDetailFragment) write:

@Override
public DetailPresenter getPresenter()
{
  return mCreditPresenter;
}

Upvotes: 1

Related Questions