package vandy.mooc.model;
import java.lang.ref.WeakReference;
import vandy.mooc.MVP;
import vandy.mooc.model.aidl.DownloadCall;
import vandy.mooc.model.aidl.DownloadRequest;
import vandy.mooc.model.aidl.DownloadResults;
import vandy.mooc.model.services.DownloadBoundServiceAsync;
import vandy.mooc.model.services.DownloadBoundServiceSync;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
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 GenericPresenter framework.
*/
public class DownloadImageModel
implements MVP.ProvidedModelOps {
/**
* Debugging tag used by the Android logger.
*/
protected final static String TAG =
DownloadImageModel.class.getSimpleName();
/**
* A WeakReference used to access methods in the Presenter layer.
* The WeakReference enables garbage collection.
*/
protected WeakReference<MVP.RequiredPresenterOps> mPresenter;
/**
* The AIDL Interface that's used to make twoway calls to the
* DownloadServiceSync Service. This object plays the role of
* Requestor in the Broker Pattern. If it's null then there's no
* connection to the Service.
*/
DownloadCall mDownloadCall;
/**
* The AIDL Interface that we will use to make oneway calls to the
* DownloadServiceAsync Service. This plays the role of Requestor
* in the Broker Pattern. If it's null then there's no connection
* to the Service.
*/
DownloadRequest mDownloadRequest;
/**
* This ServiceConnection is used to receive results after binding
* to the DownloadServiceSync Service using bindService().
*/
ServiceConnection mServiceConnectionSync =
new ServiceConnection() {
/**
* Cast the returned IBinder object to the DownloadCall
* AIDL Interface and store it for later use in
* mDownloadCall.
*/
@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
Log.d(TAG, "ComponentName: " + name);
// Call the generated stub method to convert the
// service parameter into an interface that can be
// used to make RPC calls to the Service.
mDownloadCall =
DownloadCall.Stub.asInterface(service);
}
/**
* Called if the remote service crashes and is no longer
* available. The ServiceConnection will remain bound,
* but the service will not respond to any requests.
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mDownloadCall = null;
}
};
/**
* This ServiceConnection is used to receive results after binding
* to the DownloadServiceAsync Service using bindService().
*/
ServiceConnection mServiceConnectionAsync =
new ServiceConnection() {
/**
* Cast the returned IBinder object to the DownloadRequest
* AIDL Interface and store it for later use in
* mDownloadRequest.
*/
@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
// Call the generated stub method to convert the
// service parameter into an interface that can be
// used to make RPC calls to the Service.
mDownloadRequest =
DownloadRequest.Stub.asInterface(service);
}
/**
* Called if the remote service crashes and is no longer
* available. The ServiceConnection will remain bound,
* but the service will not respond to any requests.
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mDownloadRequest = null;
}
};
/**
* The implementation of the DownloadResults AIDL
* Interface. Should be passed to the DownloadBoundServiceAsync
* Service using the DownloadRequest.downloadImage() method.
*
* This implementation of DownloadResults.Stub plays the role of
* Invoker in the Broker Pattern.
*/
DownloadResults.Stub mDownloadResults =
new DownloadResults.Stub() {
/**
* Called when the DownloadBoundServiceAsync finishes
* downloading the requested image.
*/
@Override
public void sendPath(final Uri pathToImageFile)
throws RemoteException {
mPresenter.get().displayImage(pathToImageFile);
}
};
/**
* Hook method called when a new ImageModel instance is created.
* Simply forward to the implementation.
*
* @param presenter
* A reference to the Presenter layer.
*/
@Override
public void onCreate(MVP.RequiredPresenterOps presenter) {
// Set the WeakReference.
mPresenter = new WeakReference<>(presenter);
// Bind this activity to the DownloadBoundService* Services if
// they aren't already bound Use mBoundSync/mBoundAsync
if (mDownloadCall == null) {
mPresenter.get()
.getApplicationContext()
.bindService(DownloadBoundServiceSync.makeIntent
(mPresenter.get()
.getActivityContext()),
mServiceConnectionSync,
Context.BIND_AUTO_CREATE);
Log.d(TAG,
"Calling bindService() on DownloadBoundServiceSync");
}
if (mDownloadRequest == null) {
mPresenter.get()
.getApplicationContext()
.bindService(DownloadBoundServiceAsync.makeIntent
(mPresenter.get()
.getActivityContext()),
mServiceConnectionAsync,
Context.BIND_AUTO_CREATE);
Log.d(TAG,
"Calling bindService() on DownloadBoundServiceAsync");
}
}
/**
* Hook method called to shutdown the Presenter layer.
*/
@Override
public void onDestroy(boolean isChangingConfigurations) {
// Don't bother unbinding the service if we're simply changing
// configurations.
if (isChangingConfigurations)
Log.d(TAG,
"Simply changing configurations, no need to destroy the Service");
else {
// Unbind the Sync/Async Services if they are bound. Use
// mBoundSync/mBoundAsync
if (mDownloadCall != null)
mPresenter.get()
.getApplicationContext()
.unbindService(mServiceConnectionSync);
if (mDownloadRequest != null)
mPresenter.get()
.getApplicationContext()
.unbindService(mServiceConnectionAsync);
}
}
/**
* Initiate the asynchronous image download.
*/
public void downloadImageAsync(Uri uri) {
if (mDownloadRequest != null) {
try {
Log.d(TAG,
"Calling oneway DownloadServiceAsync.downloadImage()");
// Call downloadImage() on mDownloadRequest, passing
// in the appropriate Uri and Results.
mDownloadRequest.downloadImage(uri,
mDownloadResults);
} catch(RemoteException e) {
e.printStackTrace();
}
} else
Log.d(TAG,
"mDownloadRequest is null");
}
/**
* Initiate the synchronous image download.
*/
public void downloadImageSync(Uri uri) {
if (mDownloadCall != null) {
Log.d(TAG,
"Calling twoway DownloadServiceSync.downloadImage()");
/**
* Define an AsyncTask instance to avoid blocking the UI Thread.
* */
new AsyncTask<Uri, Void, Uri>() {
/**
* Runs in a background thread.
*/
@Override
protected Uri doInBackground(Uri... params) {
try {
return mDownloadCall.downloadImage(params[0]);
} catch(RemoteException e) {
e.printStackTrace();
}
return null;
}
/**
* Runs in the UI Thread.
*/
@Override
protected void onPostExecute(Uri result) {
mPresenter.get().displayImage(result);
}
}.execute(uri);
} else
Log.d(TAG,
"mDownloadCall is null");
}
}