package vandy.mooc.presenter; import java.lang.ref.WeakReference; import vandy.mooc.MVP; import vandy.mooc.common.GenericAsyncTask; import vandy.mooc.common.GenericAsyncTaskOps; import vandy.mooc.common.GenericPresenter; import vandy.mooc.model.WeatherModel; import vandy.mooc.model.aidl.WeatherData; import android.content.Context; import android.os.Handler; /** * This class implements all the weather-related operations defined in * the WeatherPresenter interface. It implements the various Ops * interfaces so it can be created/managed by the GenericActivity * framework. It plays the role of the "Presenter" in the * Model-View-Presenter pattern. */ public class WeatherPresenter extends GenericPresenter<MVP.RequiredPresenterOps, MVP.ProvidedModelOps, WeatherModel> implements GenericAsyncTaskOps<String, Void, WeatherData>, MVP.ProvidedPresenterOps, MVP.RequiredPresenterOps { /** * Debugging tag used by the Android logger. */ protected final static String TAG = WeatherPresenter.class.getSimpleName(); /** * Used to enable garbage collection. */ protected WeakReference<MVP.RequiredViewOps> mView; /** * The GenericAsyncTask used to obtain the weather in a background * thread via the Weather Service web service. */ private GenericAsyncTask<String, Void, WeatherData, WeatherPresenter> mAsyncTask; /** * This Handler is used to post Runnables to the UI from the * mWeatherResults callback methods to avoid a dependency on the * Activity, which may be destroyed in the UI Thread during a * runtime configuration change. */ private final Handler mDisplayHandler = new Handler(); /** * Location we're trying to get current weather for. */ private String mLocation; /** * Default constructor. */ public WeatherPresenter() { } /** * Hook method called when a new instance of AcronymPresenter is * created. One time initialization code goes here, e.g., storing * a WeakReference to the View layer and initializing the Model * layer. * * @param view * A reference to the View layer. */ @Override public void onCreate(MVP.RequiredViewOps view) { // Set the WeakReference. mView = new WeakReference<>(view); // Invoke the special onCreate() method in GenericPresenter, // passing in the WeatherModel class to instantiate/manage and // "this" to provide WeatherModel with this // MVP.RequiredModelOps instance. super.onCreate(WeatherModel.class, this); } /** * Hook method dispatched by the GenericActivity framework to * initialize the ImagePresenter object after a runtime * configuration change. * * @param view The currently active ImagePresenter.View. */ @Override public void onConfigurationChange(MVP.RequiredViewOps view) { // Reset the mView WeakReference. mView = new WeakReference<>(view); } /** * Hook method called to shutdown the Presenter layer. * * @param isChangeConfigurations * True if a runtime configuration triggered the onDestroy() call. */ @Override public void onDestroy(boolean isChangingConfigurations) { // Destroy the model. getModel().onDestroy(isChangingConfigurations); } /** * Initiate the asynchronous weather lookup when the user presses * the "Look Up Async" button. */ public boolean getWeatherAsync(String location) { return getModel().getWeatherAsync(location); } /** * Initiate the synchronous weather lookup when the user presses * the "Look Up Sync" button. */ public boolean getWeatherSync(String location) { // Check to see if there's already a call in progress. if (mAsyncTask != null) return false; else { // Create and execute the AsyncTask to expand the weather // without blocking the caller. mAsyncTask = new GenericAsyncTask<>(this); mAsyncTask.execute(location); return true; } } /** * Retrieve the expanded weather results via a synchronous two-way * method call, which runs in a background thread to avoid * blocking the UI thread. */ public WeatherData doInBackground(String... locations) { mLocation = locations[0]; return getModel().getWeatherSync(mLocation); } /** * Display the results in the UI Thread. */ public void onPostExecute(WeatherData weatherData) { mView.get().displayResults(weatherData, "No weather data for location \"" + mLocation + "\" found"); mAsyncTask = null; } /** * Forwards to the View layer to displays the weather data to the * user. * * @param weatherData * WeatherData to display */ public void displayResults(final WeatherData weatherData, final String reason) { // Since the Android Binder framework dispatches this method // in a separate Thread we need to explicitly post a runnable // containing the results to the UI Thread, where it's // displayed. mDisplayHandler.post(new Runnable() { public void run() { mView.get().displayResults(weatherData, reason); } }); } /** * Return the Activity context. */ @Override public Context getActivityContext() { return mView.get().getActivityContext(); } /** * Return the Application context. */ @Override public Context getApplicationContext() { return mView.get().getApplicationContext(); } }