package edu.vandy.model; import java.lang.ref.WeakReference; import java.util.List; import edu.vandy.MVP; import edu.vandy.common.GenericServiceConnection; import edu.vandy.model.aidl.AcronymCall; import edu.vandy.model.aidl.AcronymExpansion; import edu.vandy.model.aidl.AcronymRequest; import edu.vandy.model.aidl.AcronymResults; import edu.vandy.model.services.AcronymServiceSync; import android.content.Context; import android.os.RemoteException; import android.util.Log; /** * This class plays the "Model" role in the Model-View-Presenter (MVP) * pattern by defining an interface for providing data that will be * acted upon by the "Presenter" and "View" layers in the MVP pattern. * It implements the MVP.ProvidedModelOps so it can be created/managed * by the GenericModel framework. */ public class AcronymModel implements MVP.ProvidedModelOps { /** * Debugging tag used by the Android logger. */ protected final static String TAG = AcronymModel.class.getSimpleName(); /** * A WeakReference used to access methods in the Presenter layer. * The WeakReference enables garbage collection. */ private WeakReference<MVP.RequiredPresenterOps> mAcronymPresenter; /** * This GenericServiceConnection is used to receive results after * binding to the AcronymServiceSync Service using bindService(). */ private GenericServiceConnection<AcronymCall> mServiceConnectionSync; /** * This GenericServiceConnection is used to receive results after * binding to the AcronymServiceAsync Service using bindService(). */ private GenericServiceConnection<AcronymRequest> mServiceConnectionAsync; /** * Hook method called when a new instance of AcronymModel is * created. One time initialization code goes here, e.g., storing * a WeakReference to the Presenter and initializing the sync and * async Services. * * @param presenter * A reference to the Presenter layer. */ @Override public void onCreate(MVP.RequiredPresenterOps presenter) { // Set the WeakReference. mAcronymPresenter = new WeakReference<>(presenter); // Initialize the GenericServiceConnection objects. mServiceConnectionSync = new GenericServiceConnection<>(AcronymCall.class); mServiceConnectionAsync = new GenericServiceConnection<>(AcronymRequest.class); // Bind to the sync and async Services. bindServices(); } /** * Hook method called to shutdown the Model layer. * * @param isChangingConfigurations * True if a runtime configuration triggered the onDestroy() call. */ @Override public void onDestroy(boolean isChangingConfigurations) { if (isChangingConfigurations) Log.d(TAG, "just a configuration change - unbindService() not called"); else // Unbind from the Services only if onDestroy() is not // triggered by a runtime configuration change. unbindServices(); } /** * Initiate the protocol for binding the Services. */ private void bindServices() { Log.d(TAG, "calling bindService()"); // Launch the Acronym Bound Services if they aren't already // running via a call to bindService(), which binds this // activity to the AcronymService* if they aren't already // bound. if (mServiceConnectionSync.getInterface() == null) mAcronymPresenter.get() .getApplicationContext() .bindService (AcronymServiceSync.makeIntent (mAcronymPresenter.get() .getActivityContext()), mServiceConnectionSync, Context.BIND_AUTO_CREATE); if (mServiceConnectionAsync.getInterface() == null) mAcronymPresenter.get() .getApplicationContext() .bindService (edu.vandy.model.services.AcronymServiceAsync.makeIntent (mAcronymPresenter.get().getActivityContext()), mServiceConnectionAsync, Context.BIND_AUTO_CREATE); } /** * Initiate the protocol for unbinding the Services. */ private void unbindServices() { Log.d(TAG, "calling unbindService()"); // Unbind the Async Service if it is connected. if (mServiceConnectionAsync.getInterface() != null) mAcronymPresenter.get() .getApplicationContext() .unbindService (mServiceConnectionAsync); // Unbind the Sync Service if it is connected. if (mServiceConnectionSync.getInterface() != null) mAcronymPresenter.get() .getApplicationContext() .unbindService (mServiceConnectionSync); } /** * Use a two-way synchronous AIDL call to expand the @a acronym * parameter. Must be called in a background thread (e.g., via * AsyncTask). */ @Override public List<AcronymExpansion> getAcronymExpansions(String acronym) { try { final AcronymCall acronymCall = mServiceConnectionSync.getInterface(); if (acronymCall != null) // Invoke a two-way AIDL call, which blocks the // caller. return acronymCall.expandAcronym(acronym); else Log.d(TAG, "mAcronymCall was null."); } catch (RemoteException e) { e.printStackTrace(); } return null; } /** * Use a two-way asynchronous AIDL call to expand the @a acronym * parameter. Need not be called in a background thread since the * caller isn't blocked. */ public void getAcronymExpansions(String acronym, AcronymResults results) { // Get a reference to the AcronymRequest interface. final AcronymRequest acronymRequest = mServiceConnectionAsync.getInterface(); if (acronymRequest != null) { try { // Invoke a one-way AIDL call that doesn't block the // caller. Results are returned via the sendResults() // or sendError() methods of the AsyncResultsImpl // callback object, which runs in a Thread from the // Thread pool managed by the Binder framework. acronymRequest.expandAcronym(acronym, new AsyncResultsImpl(results)); } catch (RemoteException e) { Log.e(TAG, "RemoteException:" + e.getMessage()); } } else Log.d(TAG, "acronymRequest was null."); } /** * The implementation of the AcronymResults AIDL Interface, which * will be passed to the Acronym Web service using the * AcronymRequest.expandAcronym() method. Instances of this class * play the role of Invoker in the Broker Pattern since it * dispatches the upcall to sendResults(). */ private static class AsyncResultsImpl extends AcronymResults.Stub { /** * A WeakReference to the AcronymResults object. */ private WeakReference<AcronymResults> mAcronymResults; /** * Constructor initializes the field. */ public AsyncResultsImpl(AcronymResults acronymResults) { mAcronymResults = new WeakReference<>(acronymResults); } /** * This method is invoked by AcronymServiceAsync to trigger * returning the results back to AcronymExpansionActivity. */ @Override public void sendResults(final List<AcronymExpansion> acronymExpansions) throws RemoteException { mAcronymResults.get().sendResults(acronymExpansions); } /** * This method is invoked by AcronymServiceAsync to trigger * returning error results back to AcronymExpansionActivity. */ @Override public void sendError(final String reason) throws RemoteException { mAcronymResults.get().sendError(reason); } } }