package vandy.mooc.common;
import android.content.Context;
import android.util.Log;
/**
* This Activity provides a framework for mediating access to a object
* residing in the Presenter layer in the Model-View-Presenter (MVP)
* pattern. It automatically handles runtime configuration changes in
* conjunction with an instance of PresenterType, which must implement
* the PresenterOps interface. It extends LifecycleLoggingActivity so
* that all lifecycle hook method calls are automatically logged. It
* also implements the ContextView interface that provides access to
* the Activity and Application contexts in the View layer.
*
* The three types used by a GenericActivity are the following:
* <ol>
* <li><code>RequiredViewOps</code>, the class or interface that
* defines the methods available to the Presenter object from the
* View layer.</li>
* <li><code>ProvidedPresenterOps</code>, the class or interface
* that defines the methods available to the View layer from the
* Presenter object.</li>
* <li><code>PresenterType</code>, the class created/used by the
* GenericActivity framework to implement an Presenter object.</li>
* </ol>
*/
public abstract class GenericActivity<RequiredViewOps,
ProvidedPresenterOps,
PresenterType extends PresenterOps<RequiredViewOps>>
extends LifecycleLoggingActivity
implements ContextView {
/**
* Used to retain the ProvidedPresenterOps state between runtime
* configuration changes.
*/
private final RetainedFragmentManager mRetainedFragmentManager
= new RetainedFragmentManager(this.getFragmentManager(),
TAG);
/**
* Instance of the Presenter type.
*/
private PresenterType mPresenterInstance;
/**
* Initialize or reinitialize the Presenter layer. This must be
* called *after* the onCreate(Bundle saveInstanceState) method.
*
* @param opsType
* Class object that's used to create a Presenter object.
* @param view
* Reference to the RequiredViewOps object in the View layer.
*/
public void onCreate(Class<PresenterType> opsType,
RequiredViewOps view) {
// Handle configuration-related events, including the initial
// creation of an Activity and any subsequent runtime
// configuration changes.
try {
// If this method returns true it's the first time the
// Activity has been created.
if (mRetainedFragmentManager.firstTimeIn()) {
Log.d(TAG,
"First time calling onCreate()");
// Initialize the GenericActivity fields.
initialize(opsType,
view);
} else {
Log.d(TAG,
"Second (or subsequent) time calling onCreate()");
// The RetainedFragmentManager was previously
// initialized, which means that a runtime
// configuration change occurred.
reinitialize(opsType,
view);
}
} catch (InstantiationException
| IllegalAccessException e) {
Log.d(TAG,
"onCreate() "
+ e);
// Propagate this as a runtime exception.
throw new RuntimeException(e);
}
}
/**
* Return the initialized ProvidedPresenterOps instance for use by
* application logic in the View layer.
*/
@SuppressWarnings("unchecked")
public ProvidedPresenterOps getPresenter() {
return (ProvidedPresenterOps) mPresenterInstance;
}
/**
* Return the RetainedFragmentManager.
*/
public RetainedFragmentManager getRetainedFragmentManager() {
return mRetainedFragmentManager;
}
/**
* Initialize the GenericActivity fields.
* @throws IllegalAccessException
* @throws InstantiationException
*/
private void initialize(Class<PresenterType> opsType,
RequiredViewOps view)
throws InstantiationException, IllegalAccessException {
// Create the PresenterType object.
mPresenterInstance = opsType.newInstance();
// Put the PresenterInstance into the RetainedFragmentManager under
// the simple name.
mRetainedFragmentManager.put(opsType.getSimpleName(),
mPresenterInstance);
// Perform the first initialization.
mPresenterInstance.onCreate(view);
}
/**
* Reinitialize the GenericActivity fields after a runtime
* configuration change.
* @throws IllegalAccessException
* @throws InstantiationException
*/
private void reinitialize(Class<PresenterType> opsType,
RequiredViewOps view)
throws InstantiationException, IllegalAccessException {
// Try to obtain the PresenterType instance from the
// RetainedFragmentManager.
mPresenterInstance =
mRetainedFragmentManager.get(opsType.getSimpleName());
// This check shouldn't be necessary under normal
// circumstances, but it's better to lose state than to
// crash!
if (mPresenterInstance == null)
// Initialize the GenericActivity fields.
initialize(opsType,
view);
else
// Inform it that the runtime configuration change has
// completed.
mPresenterInstance.onConfigurationChange(view);
}
/**
* Return the Activity context.
*/
@Override
public Context getActivityContext() {
return this;
}
/**
* Return the Application context.
*/
@Override
public Context getApplicationContext() {
return super.getApplicationContext();
}
}