package vandy.mooc.presenter;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.List;
import retrofit.Callback;
import retrofit.RestAdapter;
import retrofit.RestAdapter.LogLevel;
import retrofit.RetrofitError;
import retrofit.client.OkClient;
import retrofit.client.Response;
import vandy.mooc.common.ConfigurableOps;
import vandy.mooc.common.ContextView;
import vandy.mooc.common.GenericAsyncTask;
import vandy.mooc.common.GenericAsyncTaskOps;
import vandy.mooc.model.AcronymData;
import vandy.mooc.model.AcronymData.AcronymExpansion;
import vandy.mooc.model.AcronymWebServiceProxy;
import android.content.Context;
import android.util.Log;
import com.squareup.okhttp.Cache;
import com.squareup.okhttp.OkHttpClient;
/**
* This class implements all the acronym-related operations. It
* implements ConfigurableOps so it can be created/managed by the
* GenericActivity framework. It implements Callback so it can serve
* as the target of an asynchronous Retrofit RPC call. It extends
* GenericAsyncTaskOps so its doInBackground() method runs in a
* background task.
*/
public class AcronymOps
implements GenericAsyncTaskOps<String, Void, List<AcronymExpansion>>,
ConfigurableOps<AcronymOps.View>,
Callback<List<AcronymData>> {
/**
* Debugging tag used by the Android logger.
*/
protected final static String TAG =
AcronymOps.class.getSimpleName();
/**
* This interface defines the minimum interface needed by the
* AcronymOps class in the "Presenter" layer to interact with the
* AcronymActivity in the "View" layer.
*/
public interface View extends ContextView {
/**
* Start a new Activity that displays the Acronym Expansions
* to the user.
*
* @param results
* List of AcronymExpansions to display.
*/
void displayResults(List<AcronymExpansion> results,
String errorMessage);
}
/**
* Name of the filename used for the cache.
*/
private final static String CACHE_FILENAME =
"acronym_service_responses";
/**
* Used to enable garbage collection.
*/
private WeakReference<AcronymOps.View> mAcronymView;
/**
* HttpResponseCache that will cache responses from
* Network calls and data
* will expire after a certain timeout.
*/
private Cache mCache;
/**
* Client used by Retrofit to make network calls. It manages the
* cached responses and removes expired data.
*/
private OkHttpClient mOkHttpClient;
/**
* It allows access to application-specific resources.
*/
private Context mContext;
/**
* Retrofit proxy that sends requests to the Acronym web service
* and converts the Json response to an instance of AcronymData
* POJO class.
*/
private AcronymWebServiceProxy mAcronymWebServiceProxy;
/**
* The GenericAsyncTask used to expand an acronym in a background
* thread via the Acronym web service.
*/
private GenericAsyncTask<String,
Void,
List<AcronymExpansion>,
AcronymOps> mAsyncTask;
/**
* Keeps track of whether a call is already in progress and
* ignores subsequent calls until the first call is done.
*/
private boolean mCallInProgress;
/**
* Store Acronym for error reporting purposes.
*/
private String mAcronym;
/**
* Default constructor is needed by the GenericActivity framework.
*/
public AcronymOps() {
}
/**
* Hook method dispatched by the GenericActivity framework to
* initialize the AcronymOps object after it's been created.
*
* @param view The currently active AcronymOps.View.
* @param firstTimeIn Set to "true" if this is the first time the
* Ops class is initialized, else set to
* "false" if called after a runtime
* configuration change.
*/
public void onConfiguration(AcronymOps.View view,
boolean firstTimeIn) {
Log.d(TAG,
"onConfiguration() called");
// Reset the mAcronymView WeakReference.
mAcronymView =
new WeakReference<>(view);
if (firstTimeIn) {
// Store the Application context to avoid problems with
// the Activity context disappearing during a rotation.
mContext = view.getApplicationContext();
// Set up the HttpResponse cache that will be used by
// Retrofit.
mCache = new Cache(new File(mContext.getCacheDir(),
CACHE_FILENAME),
// Cache stores up to 1 MB.
1024 * 1024);
// Set up the client that will use this cache. Retrofit
// will use okhttp client to make network calls.
mOkHttpClient = new OkHttpClient();
if (mCache != null)
mOkHttpClient.setCache(mCache);
// Create a proxy to access the Acronym Service web
// service.
mAcronymWebServiceProxy =
new RestAdapter.Builder()
.setEndpoint(AcronymWebServiceProxy.ENDPOINT)
.setClient(new OkClient(mOkHttpClient))
// .setLogLevel(LogLevel.FULL)
.setLogLevel(LogLevel.NONE)
.build()
.create(AcronymWebServiceProxy.class);
}
}
/**
* Initiate the synchronous acronym lookup when the user presses
* the "Lookup Acronym Async" button. It uses Asynchronous
* Retrofit Callbacks.
*/
public boolean expandAcronymAsync(final String acronym) {
if (mCallInProgress)
return false;
else {
// Don't allow concurrent calls to get the weather.
mCallInProgress = true;
// Store this for error reporting purposes.
mAcronym = acronym;
// Get the results from Acronym Web service using a
// two-way asynchronous Retrofit RPC call.
mAcronymWebServiceProxy.getAcronymResults(acronym,
this);
return true;
}
}
/**
* Called by Retrofit for handling error.
*/
@Override
public void failure(RetrofitError error) {
Log.v(TAG,
"Retrofit failure");
// Handle a failure result.
handleResults(null,
mAcronym);
}
/**
* Called by Retrofit for handling success result.
*/
@Override
public void success(List<AcronymData> results,
Response response) {
Log.v(TAG,
"Retrofit success");
// Handle a successful result.
handleResults(results.get(0).getLfs(),
mAcronym);
}
/**
* Initiate the acronym lookup when the user presses the "Lookup
* Acronym Sync" button.
*/
public boolean expandAcronymSync(final String acronym) {
if (mCallInProgress)
return false;
else {
// Don't allow concurrent calls to expand the acronym.
mCallInProgress = true;
if (mAsyncTask != null)
// Cancel an ongoing operation to avoid having two
// requests run concurrently.
mAsyncTask.cancel(true);
// Execute the AsyncTask to expand the acronym without
// blocking the caller.
mAsyncTask = new GenericAsyncTask<>(this);
mAsyncTask.execute(acronym);
return true;
}
}
/**
* Retrieve the expanded acronym results via a synchronous two-way
* Retrofit method call to get the Acronym expansions from the
* Acronym web service, which runs in a background thread to avoid
* blocking the UI thread.
*/
public List<AcronymExpansion> doInBackground(String... acronym) {
try {
// Store the acronym for error reporting purposes.
mAcronym = acronym[0];
// Get the results from Acronym Web service using Retrofit
// and return the "long forms" containing the expansions
// of the acronym.
return mAcronymWebServiceProxy
.getAcronymResults(mAcronym)
.get(0)
.getLfs();
} catch (Exception e) {
Log.v(TAG,
"doInBackground() "
+ e);
return null;
}
}
/**
* Display the results in the UI thread.
*/
public void onPostExecute(List<AcronymExpansion> results) {
// Display the results.
handleResults(results,
mAcronym);
}
/**
* Handle the results by putting them into the cache (if they
* aren't null) and displaying them.
*/
private void handleResults(List<AcronymExpansion> results,
String acronym) {
// Try to display the results.
mAcronymView.get().displayResults(results,
"no expansions for "
+ acronym
+ " found");
// Allow another call to proceed when this method returns.
mCallInProgress = false;
}
}