/* * @copyright 2011 Philip Warner * @license GNU General Public License * * This file is part of Book Catalogue. * * Book Catalogue is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Book Catalogue is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Book Catalogue. If not, see <http://www.gnu.org/licenses/>. */ package com.eleybourn.bookcatalogue; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnKeyListener; import android.os.Bundle; import android.view.KeyEvent; import android.widget.Toast; import com.eleybourn.bookcatalogue.TaskManager.TaskManagerController; import com.eleybourn.bookcatalogue.TaskManager.TaskManagerListener; import com.eleybourn.bookcatalogue.compat.BookCatalogueActivity; import com.eleybourn.bookcatalogue.debug.Tracker; import com.eleybourn.bookcatalogue.debug.Tracker.States; import com.eleybourn.bookcatalogue.utils.Logger; /** * TODO: Remove this!!!! Fragments makes ActivityWithTasks mostly redundant. * * Class to used as a base class for any Activity that wants to run one or more threads that * use a ProgressDialog. * * Part of three components that make this easier: * - TaskManager -- handles the management of multiple threads sharing a progressDialog * - ActivityWithTasks -- uses a TaskManager (and communicates with it) to handle progress * messages for threads. Deals with orientation changes in cooperation with TaskManager. * - ManagedTask -- Background task that is managed by TaskManager and uses TaskManager to * do all display activities. * * @author Philip Warner */ abstract public class ActivityWithTasks extends BookCatalogueActivity { /** ID of associated TaskManager */ protected long mTaskManagerId = 0; /** Associated TaskManager */ private TaskManager mTaskManager = null; /** ProgressDialog for this activity */ protected ProgressBase mProgressDialog = null; /** Max value for ProgressDialog */ private int mProgressMax = 0; /** Current value for ProgressDialog */ private int mProgressCount = 0; /** Message for ProgressDialog */ private String mProgressMessage = ""; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Restore mTaskManagerId if present if (savedInstanceState != null) { mTaskManagerId = savedInstanceState.getLong("TaskManagerId"); }; } /** * Trivial internal class to implement our base progress object * * @author pjw */ private class ProgressBase extends ProgressDialog { public ProgressBase(Context context) { super(context); this.setCancelable(false); this.setCanceledOnTouchOutside(false); } } /** * ProgressDialog for Indeterminate states. * * @author pjw */ private class ProgressIndet extends ProgressBase { public ProgressIndet(Context context) { super(context); this.setIndeterminate(true); this.setProgressStyle(ProgressDialog.STYLE_SPINNER); } }; /** * ProgressDialog for Determinate states. * * @author pjw */ private class ProgressDet extends ProgressBase { public ProgressDet(Context context) { super(context); this.setIndeterminate(false); this.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); } }; /** * Utility routine to get the task manager for his activity * @return */ protected TaskManager getTaskManager() { if (mTaskManager == null) { if (mTaskManagerId != 0) { TaskManagerController c = TaskManager.getMessageSwitch().getController(mTaskManagerId); if (c != null) { mTaskManager = c.getManager(); } else { Logger.logError(new RuntimeException("Have ID, but can not find controller getting TaskManager")); } } else { //Logger.logError(new RuntimeException("Task manager requested, but no ID available")); } // Create if necessary if (mTaskManager == null) { TaskManager tm = new TaskManager(); mTaskManagerId = tm.getSenderId(); mTaskManager = tm; } } return mTaskManager; } @Override protected void onPause() { super.onPause(); // Stop listening if (mTaskManagerId != 0) { TaskManager.getMessageSwitch().removeListener(mTaskManagerId, mTaskListener); // If it's finishing, the remove all tasks and cleanup if (isFinishing()) { TaskManager tm = getTaskManager(); if (tm != null) tm.close(); } } if (mProgressDialog != null) { mProgressDialog.dismiss(); mProgressDialog = null; } } @Override protected void onResume() { super.onResume(); // If we are finishing, we don't care about active tasks. if (!this.isFinishing()) { // Restore mTaskManager if present getTaskManager(); // Listen TaskManager.getMessageSwitch().addListener(mTaskManagerId, mTaskListener, true); } } /** * Method to allow subclasses easy access to terminating tasks * * @param task */ public void onTaskEnded(ManagedTask task) { } /** * Object to handle all TaskManager events */ private TaskManagerListener mTaskListener = new TaskManagerListener() { @Override public void onTaskEnded(TaskManager manager, ManagedTask task) { // Just pass this one on ActivityWithTasks.this.onTaskEnded(task); } @Override public void onProgress(int count, int max, String message) { // RELEASE: Remove these lines! String dbgMsg = count + "/" + max + ", '" + message.replace("\n", "\\n") + "'"; Tracker.handleEvent(ActivityWithTasks.this, "SearchProgress " + dbgMsg, States.Running); System.out.println("PRG: " + dbgMsg); // Save the details mProgressCount = count; mProgressMax = max; mProgressMessage = message; // If empty, close any dialog if ((mProgressMessage == null || mProgressMessage.trim().length() == 0) && mProgressMax == mProgressCount) { if (mProgressDialog != null) { mProgressDialog.dismiss(); mProgressDialog = null; } } else { updateProgress(); } } /** * Display a Toast message */ @Override public void onToast(String message) { Toast.makeText(ActivityWithTasks.this, message, Toast.LENGTH_LONG).show(); } /** * TaskManager is finishing...cleanup. */ @Override public void onFinished() { mTaskManager.close(); mTaskManager = null; mTaskManagerId = 0; } }; /** * Utility routine to standardize checking for desired dialog type. * * @return true if dialog should be determinate */ private boolean wantDeterminateProgress() { return (mProgressMax > 0); } /** * Setup the ProgressDialog according to our needs */ private void updateProgress() { boolean wantDet = wantDeterminateProgress(); if (mProgressDialog != null) { if ((wantDet && mProgressDialog instanceof ProgressIndet) || (!wantDet && mProgressDialog instanceof ProgressDet)) { mProgressDialog.dismiss(); mProgressDialog = null; } } // Create dialog if necessary if (mProgressDialog == null) { if (wantDet) { mProgressDialog = new ProgressDet(ActivityWithTasks.this); } else { mProgressDialog = new ProgressIndet(ActivityWithTasks.this); } } // Set style if (mProgressMax > 0) { mProgressDialog.setMax(mProgressMax); } // Set message; if we are cancelling we override the message if (mTaskManager.isCancelling()) { mProgressDialog.setMessage(getString(R.string.cancelling)); } else { mProgressDialog.setMessage(mProgressMessage); } // Set other attrs mProgressDialog.setOnKeyListener(mDialogKeyListener); mProgressDialog.setOnCancelListener(mCancelHandler); // Show it if necessary mProgressDialog.show(); mProgressDialog.setProgress(mProgressCount); } /** * Handler for the user cancelling the progress dialog. */ private OnCancelListener mCancelHandler = new OnCancelListener() { public void onCancel(DialogInterface i) { cancelAndUpdateProgress(); } }; /** * Wait for the 'Back' key and cancel all tasks on keyUp. */ private OnKeyListener mDialogKeyListener = new OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP) { if (keyCode == KeyEvent.KEYCODE_BACK) { // Toasting a message here makes the app look less responsive, because // the final 'Cancelled...' message is delayed too much. //Toast.makeText(ActivityWithTasks.this, R.string.cancelling, Toast.LENGTH_LONG).show(); cancelAndUpdateProgress(); return true; } } return false; } }; /** * Cancel all tasks, and if the progress is showing, update it (it will check task manager status) */ private void cancelAndUpdateProgress() { if (mTaskManager != null) mTaskManager.cancelAllTasks(); if (mProgressDialog != null && mProgressDialog.isShowing()) { updateProgress(); } } @Override /** * Save the TaskManager ID for later retrieval */ protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mTaskManagerId != 0) outState.putLong("TaskManagerId", mTaskManagerId); } }