package com.example.util.mvp.base; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import butterknife.ButterKnife; import com.example.tools.dagger.components.BaseActivityComponent; import com.hannesdorfmann.mosby.mvp.MvpActivity; import com.hannesdorfmann.mosby.mvp.MvpPresenter; import com.hannesdorfmann.mosby.mvp.MvpView; import com.hannesdorfmann.mosby.mvp.delegate.ActivityMvpDelegate; import com.hannesdorfmann.mosby.mvp.delegate.ActivityMvpViewStateDelegateCallback; import com.hannesdorfmann.mosby.mvp.delegate.ActivityMvpViewStateDelegateImpl; import com.hannesdorfmann.mosby.mvp.viewstate.MvpViewStateActivity; import com.hannesdorfmann.mosby.mvp.viewstate.ViewState; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Provider; public abstract class BaseActivity<COMPONENT extends BaseActivityComponent, VIEW extends MvpView, PRESENTER extends MvpPresenter<VIEW>, VIEW_STATE extends ViewState<VIEW>> extends AppCompatActivity implements ActivityMvpViewStateDelegateCallback<VIEW, PRESENTER> { /** * Can't inject directly, as the presenter instantiation needs to happen by mosby in {@link this#createPresenter()}. */ @Inject Provider<PRESENTER> presenterProvider; private PRESENTER presenter; /** * Can't inject directly, as the presenter instantiation needs to happen by mosby in {@link this#createViewState()}. */ @Inject Provider<VIEW_STATE> viewStateProvider; private VIEW_STATE viewState; /** * Whether we want to retain state during configuration changes. Means that the same {@link PRESENTER} and {@link VIEW_STATE} is kept. */ private boolean retainInstance; /** * Instead of extending {@link MvpActivity} or {@link MvpViewStateActivity} we are using a mosby's delegate. To do that we need to * propagate certain activity lifecycle methods to the delegate. */ @Nullable protected ActivityMvpDelegate mvpDelegate; private boolean viewStateRestoreInProgress; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { this.retainInstance = true; super.onCreate(savedInstanceState); setContentView(getLayoutId()); ButterKnife.bind(this); COMPONENT component = constructComponent(); component.inject(this); getMvpDelegate().onCreate(savedInstanceState); } protected abstract int getLayoutId(); protected abstract COMPONENT constructComponent(); // Delegate propagation **************************************************************************************************************** private ActivityMvpDelegate<VIEW, PRESENTER> getMvpDelegate() { if (mvpDelegate == null) { mvpDelegate = new ActivityMvpViewStateDelegateImpl<>(this); } return mvpDelegate; } @Override protected void onDestroy() { super.onDestroy(); getMvpDelegate().onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); getMvpDelegate().onSaveInstanceState(outState); } @Override protected void onPause() { super.onPause(); getMvpDelegate().onPause(); } @Override protected void onResume() { super.onResume(); getMvpDelegate().onResume(); } @Override protected void onStart() { super.onStart(); getMvpDelegate().onStart(); } @Override protected void onStop() { super.onStop(); getMvpDelegate().onStop(); } @Override protected void onRestart() { super.onRestart(); getMvpDelegate().onRestart(); } @Override public void onContentChanged() { super.onContentChanged(); getMvpDelegate().onContentChanged(); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); getMvpDelegate().onPostCreate(savedInstanceState); } /** * Mosby stores 3 things in the custom non configuration instance ({@link AppCompatActivity#onRetainCustomNonConfigurationInstance()}) * during a configuration change: * - The {@link PRESENTER} * - The {@link VIEW_STATE} * - A custom object defined by us via the {@link #onRetainNonMosbyCustomNonConfigurationInstance()}. * This method is simple delegation so that the mosby knows how to access the third object, the non mosby managed one. */ @Override @Nullable public Object getNonMosbyLastCustomNonConfigurationInstance() { return getMvpDelegate().getNonMosbyLastCustomNonConfigurationInstance(); } /** * Mosby stores 3 things in the custom non configuration instance ({@link AppCompatActivity#onRetainCustomNonConfigurationInstance()}) * during a configuration change: * - The {@link PRESENTER} * - The {@link VIEW_STATE} * - A custom object defined by us via the {@link #onRetainNonMosbyCustomNonConfigurationInstance()}. * This method passes control to the delegate, which simply bundles all 3 objects into a wrapper object. **/ @Override @Nullable public Object onRetainCustomNonConfigurationInstance() { return getMvpDelegate().onRetainCustomNonConfigurationInstance(); } // MVP related ************************************************************************************************************************* @Override public VIEW getMvpView() { return (VIEW) this; } @Override public PRESENTER createPresenter() { return presenterProvider.get(); } @Override public PRESENTER getPresenter() { return presenter; } @Override public void setPresenter(PRESENTER presenter) { this.presenter = presenter; } @Override public boolean isRetainInstance() { return retainInstance; } @Override public void setRetainInstance(boolean retainInstance) { this.retainInstance = retainInstance; } @Override public boolean shouldInstanceBeRetained() { return retainInstance && isChangingConfigurations(); } /** * Mosby stores 3 things in the custom non configuration instance ({@link AppCompatActivity#onRetainCustomNonConfigurationInstance()}) * during a configuration change: * - The {@link PRESENTER} * - The {@link VIEW_STATE} * - A custom object defined by us with this method. **/ @Override @Nullable public Object onRetainNonMosbyCustomNonConfigurationInstance() { // Default implementation doesn't save anything inside mosby. Override when required. return null; } // View state related ****************************************************************************************************************** @Override public void setViewState(ViewState<VIEW> viewState) { this.viewState = (VIEW_STATE) viewState; } @Override public VIEW_STATE getViewState() { return viewState; } @Override public ViewState createViewState() { return viewStateProvider.get(); } @Override public void setRestoringViewState(boolean restoringViewState) { this.viewStateRestoreInProgress = restoringViewState; } @Override public boolean isRestoringViewState() { return viewStateRestoreInProgress; } /** * Called right after the state of the view has been restored from the {@link VIEW_STATE}. */ @Override public void onViewStateInstanceRestored(boolean instanceStateRetained) { // Default not doing anything. Override when required. } @Override public void onNewViewStateInstance() { onFirstCreate(); } /** * Default implementation not doing anything. Override when required to perform long running tasks only once, then save their state in * the {@link VIEW_STATE} */ protected void onFirstCreate() { // Default implementation not doing anything. Override when required. } }