/* * Copyright 2015 Hannes Dorfmann. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hannesdorfmann.mosby3; import android.app.Activity; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.hannesdorfmann.mosby3.mvi.MviPresenter; import com.hannesdorfmann.mosby3.mvp.MvpPresenter; import com.hannesdorfmann.mosby3.mvp.MvpView; import java.util.UUID; /** * The concrete implementation of {@link ActivityMviDelegate}. * This delegate creates the Presenter and attaches the View to the presenter in {@link * Activity#onStart()}. The view is detached from presenter in {@link * Activity#onStop()} * * @param <V> The type of {@link MvpView} * @param <P> The type of {@link MvpPresenter} * @author Hannes Dorfmann * @see ActivityMviDelegate * @since 3.0 */ public class ActivityMviDelegateImpl<V extends MvpView, P extends MviPresenter<V, ?>> implements ActivityMviDelegate { public static boolean DEBUG = false; private static final String DEBUG_TAG = "ActivityMviDelegateImpl"; private static final String KEY_MOSBY_VIEW_ID = "com.hannesdorfmann.mosby3.activity.mvi.id"; private String mosbyViewId = null; private MviDelegateCallback<V, P> delegateCallback; private Activity activity; private boolean keepPresenterInstance; private P presenter; /** * Creates a new Delegate with an presenter that survives screen orientation changes * * @param activity The activity * @param delegateCallback The delegate callback */ public ActivityMviDelegateImpl(@NonNull Activity activity, @NonNull MviDelegateCallback<V, P> delegateCallback) { this(activity, delegateCallback, true); } /** * Creates a new delegate * * @param activity The activity * @param delegateCallback The delegate callback * @param keepPresenterInstance true, if the presenter instance should be kept through screen * orientation changes, false if not (a new presenter instance will be created every time you * rotate your device) */ public ActivityMviDelegateImpl(@NonNull Activity activity, @NonNull MviDelegateCallback<V, P> delegateCallback, boolean keepPresenterInstance) { if (activity == null) { throw new NullPointerException("Activity is null"); } if (delegateCallback == null) { throw new NullPointerException("MvpDelegateCallback is null!"); } this.delegateCallback = delegateCallback; this.activity = activity; this.keepPresenterInstance = keepPresenterInstance; } @Override public void onCreate(@Nullable Bundle bundle) { if (keepPresenterInstance && bundle != null) { mosbyViewId = bundle.getString(KEY_MOSBY_VIEW_ID); } if (DEBUG) { Log.d(DEBUG_TAG, "MosbyView ID = " + mosbyViewId + " for MvpView: " + delegateCallback.getMvpView()); } } @Override public void onStart() { boolean viewStateWillBeRestored = false; if (mosbyViewId == null) { // No presenter available, // Activity is starting for the first time (or keepPresenterInstance == false) presenter = createViewIdAndCreatePresenter(); if (DEBUG) { Log.d(DEBUG_TAG, "new Presenter instance created: " + presenter + " for " + delegateCallback.getMvpView()); } } else { presenter = PresenterManager.getPresenter(activity, mosbyViewId); if (presenter == null) { // Process death, // hence no presenter with the given viewState id stored, although we have a viewState id presenter = createViewIdAndCreatePresenter(); if (DEBUG) { Log.d(DEBUG_TAG, "No Presenter instance found in cache, although MosbyView ID present. This was caused by process death, therefore new Presenter instance created: " + presenter); } } else { viewStateWillBeRestored = true; if (DEBUG) { Log.d(DEBUG_TAG, "Presenter instance reused from internal cache: " + presenter); } } } // presenter is ready, so attach viewState V view = delegateCallback.getMvpView(); if (view == null) { throw new NullPointerException( "MvpView returned from getMvpView() is null. Returned by " + activity); } if (viewStateWillBeRestored) { delegateCallback.setRestoringViewState(true); } presenter.attachView(view); if (viewStateWillBeRestored) { delegateCallback.setRestoringViewState(false); } if (DEBUG) { Log.d(DEBUG_TAG, "MvpView attached to Presenter. MvpView: " + view + " Presenter: " + presenter); } } /** * Generates the unique (mosby internal) view id and calls {@link * MviDelegateCallback#createPresenter()} * to create a new presenter instance * * @return The new created presenter instance */ private P createViewIdAndCreatePresenter() { P presenter = delegateCallback.createPresenter(); if (presenter == null) { throw new NullPointerException( "Presenter returned from createPresenter() is null. Activity is " + activity); } if (keepPresenterInstance) { mosbyViewId = UUID.randomUUID().toString(); PresenterManager.putPresenter(activity, mosbyViewId, presenter); } return presenter; } @Override public void onSaveInstanceState(Bundle outState) { if (keepPresenterInstance && outState != null) { outState.putString(KEY_MOSBY_VIEW_ID, mosbyViewId); if (DEBUG) { Log.d(DEBUG_TAG, "Saving MosbyViewId into Bundle. ViewId: " + mosbyViewId); } } } /** * Determines whether or not a Presenter Instance should be kept * * @param keepPresenterInstance true, if the delegate has enabled keep */ static boolean retainPresenterInstance(boolean keepPresenterInstance, Activity activity) { return keepPresenterInstance && (activity.isChangingConfigurations() || !activity.isFinishing()); } @Override public void onStop() { boolean retainPresenterInstance = retainPresenterInstance(keepPresenterInstance, activity); presenter.detachView(retainPresenterInstance); if (DEBUG) { Log.d(DEBUG_TAG, "detached MvpView from Presenter. MvpView " + delegateCallback.getMvpView() + " Presenter: " + presenter); } if (!retainPresenterInstance){ if (mosbyViewId != null) { // mosbyViewId == null if keepPresenterInstance == false PresenterManager.remove(activity, mosbyViewId); } Log.d(DEBUG_TAG, "Destroying Presenter permanently " + presenter); } } @Override public void onDestroy() { presenter = null; activity = null; delegateCallback = null; } @Override public void onPostCreate(Bundle savedInstanceState) { } @Override public void onPause() { } @Override public void onResume() { } @Override public void onRestart() { } @Override public void onContentChanged() { } @Override public Object onRetainCustomNonConfigurationInstance() { return null; } }