package com.charlesmadere.android.classygames.server; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Build; import android.util.Log; import com.charlesmadere.android.classygames.R; import com.charlesmadere.android.classygames.models.Person; import com.charlesmadere.android.classygames.utilities.Utilities; import java.io.IOException; /** * A class that will hit an end point on the Classy Games server with some * given data. */ public abstract class ServerApi { protected final static String LOG_TAG = Utilities.LOG_TAG + " - ServerApi"; /** * A reference to this class's AsyncTask (if it's currently running). */ private ServerApiTask serverApiTask; /** * Stores whether or not the user should see a ProgressDialog popup while * this ServerApi object is running. */ private boolean showProgressDialog; /** * The Context of the class that this ServerApi class is being called from. */ private Context context; /** * Object that allows us to run any of the methods that are defined in the * Listeners interface. */ private Listeners listeners; /** * An interface that will be used throughout the lifecycle of this class. */ public interface Listeners { /** * If this class's ServerApiTask AsyncTask gets cancelled then this * method will be run. */ public void onCancel(); /** * Once this ServerApi class has finished its duty this method will * run. If the ServerApiTask AsyncTask was cancelled, or if the user * dismissed or selected "No" on the AlertDialog that this class * prompts with, then this method will never be run. * * @param serverResponse * The JSON response as received from the Classy Games. */ public void onComplete(final String serverResponse); /** * If, on the AlertDialog that this class prompts with, the user * selects either "No" or dismisses it, then this method will be run. */ public void onDismiss(); } /** * Creates a ServerApi object. This should be used to hit certain server * end points. If this constructor is used, then the user will see a * ProgressDialog popup while this ServerApi object is running. * * @param context * The Context of the class that you're creating this object from. * * @param listeners * A set of listeners to call once we're done running code here. */ protected ServerApi(final Context context, final Listeners listeners) { this(context, true, listeners); } /** * Creates a ServerApi object. This should be used to hit certain server * end points. This constructor also allows you to specify whether or not * the user should see a ProgressDialog popup while this ServerApi object * is running. * * @param context * The Context of the class that you're creating this object from. * * @param showProgressDialog * Set this to true if you want the user to see a ProgressDialog while this * ServerApi object is running. * * @param listeners * A set of listeners to call once we're done running code here. */ protected ServerApi(final Context context, final boolean showProgressDialog, final Listeners listeners) { this.context = context; this.showProgressDialog = showProgressDialog; this.listeners = listeners; } /** * @return * Returns the Context object handed in through this class's constructor. */ protected Context getContext() { return context; } /** * Cancels the currently running AsyncTask (if it is currently running). */ public void cancel() { if (serverApiTask != null) { serverApiTask.cancel(true); } listeners.onCancel(); } /** * Begins the execution of this ServerApi code. * * @param askUserToExecute * True if you want to ask the user to confirm the execution of this * ServerApi code. False if you want the code without asking the user * anything; this will immediately begin the ServerApi code. */ public void execute(final boolean askUserToExecute) { if (askUserToExecute) { final AlertDialog.Builder builder = new AlertDialog.Builder(context) .setMessage(getDialogMessage()) .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { dialog.dismiss(); listeners.onCancel(); } }) .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(final DialogInterface dialog) { dialog.dismiss(); listeners.onDismiss(); } }) .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { dialog.dismiss(); executeTask(); } }) .setTitle(getDialogTitle()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { builder.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(final DialogInterface dialog) { dialog.dismiss(); listeners.onDismiss(); } }); } builder.show(); } else { executeTask(); } } /** * Begins the execution of this ServerApi code. Will first ask the user if * they do in fact want to perform this server call. */ public void execute() { execute(true); } /** * Starts the execution of the ServerApiTask AsyncTask. */ protected void executeTask() { serverApiTask = new ServerApiTask(); serverApiTask.execute(); } /** * An AsyncTask that will query the Classy Games server. */ protected class ServerApiTask extends AsyncTask<Void, Void, String> { private ProgressDialog progressDialog; @Override protected String doInBackground(final Void... v) { String serverResponse = null; if (!isCancelled() && Utilities.checkForNetworkConnectivity(context)) { final Person whoAmI = Utilities.getWhoAmI(context); try { serverResponse = postToServer(whoAmI); } catch (final IOException e) { Log.e(LOG_TAG, "IOException during ServerApi's doInBackground()!", e); } } return serverResponse; } private void cancelled() { serverApiTask = null; if (progressDialog != null) { progressDialog.dismiss(); } listeners.onCancel(); } @Override protected void onCancelled() { cancelled(); } @Override protected void onCancelled(final String serverResponse) { cancelled(); } @Override protected void onPostExecute(final String serverResponse) { if (progressDialog != null) { progressDialog.dismiss(); } serverApiTask = null; listeners.onComplete(serverResponse); } @Override protected void onPreExecute() { if (showProgressDialog) { progressDialog = new ProgressDialog(context); progressDialog.setCancelable(true); progressDialog.setCanceledOnTouchOutside(true); progressDialog.setMessage(context.getString(getProgressDialogMessage())); progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(final DialogInterface dialog) { ServerApiTask.this.cancel(true); } }); progressDialog.setTitle(getDialogTitle()); progressDialog.show(); } } } /** * @return * The R.string.* value for the message to show in the dialog box. This * should ask the user something like "are you sure you want to blah blah?" */ protected int getDialogMessage() { return R.string.are_you_sure_that_you_want_to_send_this_data_to_the_classy_games_servers; } /** * @return * The R.string.* value for the title to show in the dialog box. */ protected int getDialogTitle() { return R.string.server_exchange; } /** * @return * The R.string.* value for the message to show in the progress dialog box. */ protected int getProgressDialogMessage() { return R.string.sending_data_to_the_classy_games_servers; } /** * The heart of the AsyncTask's code. * * @param whoAmI * A Person object that represents the user of this Android device. * * @return * A String that contains the responding result from the server or null if * a problem happened. */ protected abstract String postToServer(final Person whoAmI) throws IOException; }