mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-23 18:02:15 -05:00
Handle activity restarts due to configuration changes
Close existing dialogs before the activity is destroyed. Recreate them and rewire AsyncTasks to the new activity instance after its creation.
This commit is contained in:
parent
6e1bf2965d
commit
dfa97cd878
@ -1034,9 +1034,12 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
||||
<string name="settings_import_encryption_password_prompt">Please enter the password you used when exporting your settings:</string>
|
||||
<string name="settings_export_account">Export account settings</string>
|
||||
<string name="settings_export_all">Export settings and accounts</string>
|
||||
<string name="settings_import_dialog_title">Import</string>
|
||||
<string name="settings_export_dialog_title">Export</string>
|
||||
<string name="settings_import">Import settings</string>
|
||||
<string name="settings_exporting">Exporting settings...</string>
|
||||
<string name="settings_importing">Importing settings...</string>
|
||||
<string name="settings_import_scanning_file">Scanning file...</string>
|
||||
<string name="settings_export_success">Saved exported settings to <xliff:g id="filename">%s</xliff:g></string>
|
||||
<string name="settings_import_success">Imported <xliff:g id="accounts">%s</xliff:g> from <xliff:g id="filename">%s</xliff:g></string>
|
||||
<plurals name="settings_import_success">
|
||||
|
@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@ -20,7 +21,6 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
@ -58,6 +58,7 @@ import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.SearchAccount;
|
||||
import com.fsck.k9.SearchSpecification;
|
||||
import com.fsck.k9.activity.misc.ExtendedAsyncTask;
|
||||
import com.fsck.k9.activity.setup.AccountSettings;
|
||||
import com.fsck.k9.activity.setup.AccountSetupBasics;
|
||||
import com.fsck.k9.activity.setup.Prefs;
|
||||
@ -104,6 +105,30 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
private SearchAccount integratedInboxAccount = null;
|
||||
private FontSizes mFontSizes = K9.getFontSizes();
|
||||
|
||||
/**
|
||||
* Contains a reference to a {@link ExtendedAsyncTask} while it is running.
|
||||
*/
|
||||
private ExtendedAsyncTask<Void, Void, Boolean> mAsyncTask;
|
||||
|
||||
/**
|
||||
* Contains information about the currently displayed dialog (if available).
|
||||
*
|
||||
* <p>
|
||||
* This object is returned from {@link #onRetainNonConfigurationInstance()} if a dialog is
|
||||
* being displayed while the activity is being restarted. It is then used by the new activity
|
||||
* instance to re-create that dialog.
|
||||
* </p>
|
||||
*/
|
||||
private DialogInfo mDialogInfo;
|
||||
|
||||
/**
|
||||
* Reference to the dialog currently being displayed (if available).
|
||||
*
|
||||
* @see #showDialog(int, String)
|
||||
*/
|
||||
private AlertDialog mDialog;
|
||||
|
||||
|
||||
private static final int ACTIVITY_REQUEST_PICK_SETTINGS_FILE = 1;
|
||||
|
||||
class AccountsHandler extends Handler {
|
||||
@ -310,13 +335,16 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
Intent intent = getIntent();
|
||||
boolean startup = intent.getData() == null && intent.getBooleanExtra(EXTRA_STARTUP, true);
|
||||
onNewIntent(intent);
|
||||
|
||||
if (startup && K9.startIntegratedInbox()) {
|
||||
onOpenAccount(integratedInboxAccount);
|
||||
finish();
|
||||
return;
|
||||
} else if (startup && accounts.length == 1 && onOpenAccount(accounts[0])) {
|
||||
// fall through to "else" if !onOpenAccount()
|
||||
finish();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
|
||||
@ -334,10 +362,21 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
}
|
||||
|
||||
restoreAccountStats(icicle);
|
||||
|
||||
// Handle activity restarts because of a configuration change (e.g. rotating the screen)
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained != null) {
|
||||
// If we displayed a dialog before the configuration change, re-create it here
|
||||
if (retained instanceof DialogInfo) {
|
||||
DialogInfo dialogInfo = (DialogInfo) retained;
|
||||
showDialog(dialogInfo.headerRes, dialogInfo.message);
|
||||
}
|
||||
// If there's an ExtendedAsyncTask running, update it with the new Activity
|
||||
else if (retained instanceof ExtendedAsyncTask) {
|
||||
mAsyncTask = (ExtendedAsyncTask) retained;
|
||||
mAsyncTask.attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -389,6 +428,22 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the reference to a currently displayed dialog or a running AsyncTask (if available).
|
||||
*/
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
Object retain = null;
|
||||
if (mDialogInfo != null) {
|
||||
retain = mDialogInfo;
|
||||
dismissDialog();
|
||||
} else if (mAsyncTask != null) {
|
||||
retain = mAsyncTask;
|
||||
mAsyncTask.detach();
|
||||
}
|
||||
return retain;
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
BaseAccount[] accounts = Preferences.getPreferences(this).getAccounts();
|
||||
|
||||
@ -868,28 +923,73 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
|
||||
Log.i(K9.LOG_TAG, "onImport importing from URI " + uri.toString());
|
||||
|
||||
new ListImportContentsAsyncTask(uri, null).execute();
|
||||
mAsyncTask = new ListImportContentsAsyncTask(this, uri, null);
|
||||
mAsyncTask.execute();
|
||||
}
|
||||
|
||||
private void showDialog(final Context context, final int headerRes, final String message) {
|
||||
this.runOnUiThread(new Runnable() {
|
||||
private void asyncTaskFinished() {
|
||||
mAsyncTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores information about a dialog.
|
||||
*
|
||||
* @see Accounts#showDialog(int, String)
|
||||
* @see Accounts#onCreate(Bundle)
|
||||
*/
|
||||
private static class DialogInfo {
|
||||
public final int headerRes;
|
||||
|
||||
//TODO: "message" is already localized. This is a problem if the activity is restarted when
|
||||
// the system language was changed. We have to recreate the message string in that case.
|
||||
public final String message;
|
||||
|
||||
DialogInfo(int headerRes, String message) {
|
||||
this.headerRes = headerRes;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog.
|
||||
*
|
||||
* @param headerRes
|
||||
* The resource ID of the string that is used as title for the dialog box.
|
||||
* @param message
|
||||
* The message to display.
|
||||
*/
|
||||
private void showDialog(final int headerRes, final String message) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
// Store information about the dialog so it can be re-created when the activity is
|
||||
// restarted due to a configuration change.
|
||||
mDialogInfo = new DialogInfo(headerRes, message);
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(Accounts.this);
|
||||
builder.setTitle(headerRes);
|
||||
builder.setMessage(message);
|
||||
builder.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
mDialog = builder.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the dialog that was created using {@link #showDialog(int, String)}.
|
||||
*/
|
||||
private void dismissDialog() {
|
||||
mDialog.dismiss();
|
||||
mDialogInfo = null;
|
||||
mDialog = null;
|
||||
}
|
||||
|
||||
class AccountsAdapter extends ArrayAdapter<BaseAccount> {
|
||||
public AccountsAdapter(BaseAccount[] accounts) {
|
||||
super(Accounts.this, 0, accounts);
|
||||
@ -1112,34 +1212,39 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
})
|
||||
.show();
|
||||
*/
|
||||
new ExportAsyncTask(includeGlobals, accountUuids, null).execute();
|
||||
mAsyncTask = new ExportAsyncTask(this, includeGlobals, accountUuids, null);
|
||||
mAsyncTask.execute();
|
||||
}
|
||||
|
||||
private class ExportAsyncTask extends AsyncTask<Void, Void, Boolean> {
|
||||
/**
|
||||
* Handles exporting of global settings and/or accounts in a background thread.
|
||||
*/
|
||||
private static class ExportAsyncTask extends ExtendedAsyncTask<Void, Void, Boolean> {
|
||||
private boolean mIncludeGlobals;
|
||||
private Set<String> mAccountUuids;
|
||||
private String mEncryptionKey;
|
||||
private String mFileName;
|
||||
|
||||
private ExportAsyncTask(boolean includeGlobals, Set<String> accountUuids,
|
||||
String encryptionKey) {
|
||||
|
||||
private ExportAsyncTask(Accounts activity, boolean includeGlobals,
|
||||
Set<String> accountUuids, String encryptionKey) {
|
||||
super(activity);
|
||||
mIncludeGlobals = includeGlobals;
|
||||
mAccountUuids = accountUuids;
|
||||
mEncryptionKey = encryptionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
//TODO: show progress bar instead of displaying toast
|
||||
String toastText = Accounts.this.getString(R.string.settings_exporting);
|
||||
Toast toast = Toast.makeText(Accounts.this, toastText, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
protected void showProgressDialog() {
|
||||
String title = mContext.getString(R.string.settings_export_dialog_title);
|
||||
String message = mContext.getString(R.string.settings_exporting);
|
||||
mProgressDialog = ProgressDialog.show(mActivity, title, message, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
mFileName = StorageExporter.exportToFile(Accounts.this, mIncludeGlobals,
|
||||
mFileName = StorageExporter.exportToFile(mContext, mIncludeGlobals,
|
||||
mAccountUuids, mEncryptionKey);
|
||||
} catch (StorageImportExportException e) {
|
||||
Log.w(K9.LOG_TAG, "Exception during export", e);
|
||||
@ -1150,18 +1255,28 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
Accounts activity = (Accounts) mActivity;
|
||||
|
||||
// Let the activity know that the background task is complete
|
||||
activity.asyncTaskFinished();
|
||||
|
||||
removeProgressDialog();
|
||||
|
||||
if (success) {
|
||||
showDialog(Accounts.this, R.string.settings_export_success_header,
|
||||
Accounts.this.getString(R.string.settings_export_success, mFileName));
|
||||
activity.showDialog(R.string.settings_export_success_header,
|
||||
mContext.getString(R.string.settings_export_success, mFileName));
|
||||
} else {
|
||||
//TODO: make the exporter return an error code; translate that error code to a localized string here
|
||||
showDialog(Accounts.this, R.string.settings_export_failed_header,
|
||||
Accounts.this.getString(R.string.settings_export_failure, "Something went wrong"));
|
||||
activity.showDialog(R.string.settings_export_failed_header,
|
||||
mContext.getString(R.string.settings_export_failure, "Something went wrong"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ImportAsyncTask extends AsyncTask<Void, Void, Boolean> {
|
||||
/**
|
||||
* Handles importing of global settings and/or accounts in a background thread.
|
||||
*/
|
||||
private static class ImportAsyncTask extends ExtendedAsyncTask<Void, Void, Boolean> {
|
||||
private boolean mIncludeGlobals;
|
||||
private List<String> mAccountUuids;
|
||||
private boolean mOverwrite;
|
||||
@ -1169,8 +1284,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
private InputStream mInputStream;
|
||||
private ImportResults mImportResults;
|
||||
|
||||
private ImportAsyncTask(boolean includeGlobals, List<String> accountUuids,
|
||||
boolean overwrite, String encryptionKey, InputStream is) {
|
||||
private ImportAsyncTask(Accounts activity, boolean includeGlobals,
|
||||
List<String> accountUuids, boolean overwrite, String encryptionKey,
|
||||
InputStream is) {
|
||||
super(activity);
|
||||
mIncludeGlobals = includeGlobals;
|
||||
mAccountUuids = accountUuids;
|
||||
mOverwrite = overwrite;
|
||||
@ -1179,17 +1296,16 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
//TODO: show progress bar instead of displaying toast
|
||||
String toastText = Accounts.this.getString(R.string.settings_importing);
|
||||
Toast toast = Toast.makeText(Accounts.this, toastText, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
protected void showProgressDialog() {
|
||||
String title = mContext.getString(R.string.settings_import_dialog_title);
|
||||
String message = mContext.getString(R.string.settings_importing);
|
||||
mProgressDialog = ProgressDialog.show(mActivity, title, message, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
mImportResults = StorageImporter.importSettings(Accounts.this, mInputStream,
|
||||
mImportResults = StorageImporter.importSettings(mContext, mInputStream,
|
||||
mEncryptionKey, mIncludeGlobals, mAccountUuids, mOverwrite);
|
||||
} catch (StorageImportExportException e) {
|
||||
Log.w(K9.LOG_TAG, "Exception during export", e);
|
||||
@ -1200,49 +1316,60 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
Accounts activity = (Accounts) mActivity;
|
||||
|
||||
// Let the activity know that the background task is complete
|
||||
activity.asyncTaskFinished();
|
||||
|
||||
removeProgressDialog();
|
||||
|
||||
if (success) {
|
||||
int imported = mImportResults.importedAccounts.size();
|
||||
|
||||
//TODO: display names of imported accounts (name from file *and* possibly new name)
|
||||
|
||||
showDialog(Accounts.this, R.string.settings_import_success_header,
|
||||
activity.showDialog(R.string.settings_import_success_header,
|
||||
//FIXME: use correct file name
|
||||
Accounts.this.getString(R.string.settings_import_success, imported, "filename"));
|
||||
refresh();
|
||||
mContext.getString(R.string.settings_import_success, imported, "filename"));
|
||||
activity.refresh();
|
||||
} else {
|
||||
//TODO: make the importer return an error code; translate that error code to a localized string here
|
||||
showDialog(Accounts.this, R.string.settings_import_failed_header,
|
||||
Accounts.this.getString(R.string.settings_import_failure, "unknown", "Something went wrong"));
|
||||
activity.showDialog(R.string.settings_import_failed_header,
|
||||
mContext.getString(R.string.settings_import_failure, "unknown", "Something went wrong"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImportContents mImportContents;
|
||||
private class ListImportContentsAsyncTask extends AsyncTask<Void, Void, Boolean> {
|
||||
private static class ListImportContentsAsyncTask extends ExtendedAsyncTask<Void, Void, Boolean> {
|
||||
private Uri mUri;
|
||||
private String mEncryptionKey;
|
||||
private InputStream mInputStream;
|
||||
private ImportContents mImportContents;
|
||||
|
||||
private ListImportContentsAsyncTask(Accounts activity, Uri uri, String encryptionKey) {
|
||||
super(activity);
|
||||
|
||||
private ListImportContentsAsyncTask(Uri uri, String encryptionKey) {
|
||||
mUri = uri;
|
||||
mEncryptionKey = encryptionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
//TODO: show progress bar
|
||||
protected void showProgressDialog() {
|
||||
String title = mContext.getString(R.string.settings_import_dialog_title);
|
||||
String message = mContext.getString(R.string.settings_import_scanning_file);
|
||||
mProgressDialog = ProgressDialog.show(mActivity, title, message, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
|
||||
InputStream is = getContentResolver().openInputStream(mUri);
|
||||
mImportContents = StorageImporter.getImportStreamContents(
|
||||
Accounts.this, is, mEncryptionKey);
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
InputStream is = resolver.openInputStream(mUri);
|
||||
mImportContents = StorageImporter.getImportStreamContents(mContext, is,
|
||||
mEncryptionKey);
|
||||
|
||||
// Open another InputStream in the background. This is used later by ImportAsyncTask
|
||||
mInputStream = getContentResolver().openInputStream(mUri);
|
||||
mInputStream = resolver.openInputStream(mUri);
|
||||
|
||||
} catch (StorageImportExportException e) {
|
||||
Log.w(K9.LOG_TAG, "Exception during export", e);
|
||||
@ -1257,8 +1384,25 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
Accounts activity = (Accounts) mActivity;
|
||||
|
||||
// Let the activity know that the background task is complete
|
||||
activity.asyncTaskFinished();
|
||||
|
||||
removeProgressDialog();
|
||||
|
||||
if (success) {
|
||||
final ListView importSelectionView = new ListView(Accounts.this);
|
||||
showImportSelectionDialog();
|
||||
} else {
|
||||
//TODO: make the importer return an error code; translate that error code to a localized string here
|
||||
activity.showDialog(R.string.settings_import_failed_header,
|
||||
mContext.getString(R.string.settings_import_failure, "unknown", "Something went wrong"));
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: we need to be able to re-create this dialog after a configuration change
|
||||
private void showImportSelectionDialog() {
|
||||
final ListView importSelectionView = new ListView(mActivity);
|
||||
List<String> contents = new ArrayList<String>();
|
||||
if (mImportContents.globalSettings) {
|
||||
contents.add("Global settings");
|
||||
@ -1267,7 +1411,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
contents.add(account.name);
|
||||
}
|
||||
importSelectionView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
importSelectionView.setAdapter(new ArrayAdapter<String>(Accounts.this, android.R.layout.simple_list_item_checked, contents));
|
||||
importSelectionView.setAdapter(new ArrayAdapter<String>(mActivity, android.R.layout.simple_list_item_checked, contents));
|
||||
importSelectionView.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
@ -1276,19 +1420,20 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> arg0) {}
|
||||
public void onNothingSelected(AdapterView<?> arg0) { /* Do nothing */ }
|
||||
});
|
||||
|
||||
//TODO: listview header: "Please select the settings you wish to import"
|
||||
//TODO: listview footer: "Select all" / "Select none" buttons?
|
||||
//TODO: listview footer: "Overwrite existing accounts?" checkbox
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(Accounts.this);
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
|
||||
builder.setTitle("Import selection");
|
||||
builder.setView(importSelectionView);
|
||||
builder.setInverseBackgroundForced(true);
|
||||
builder.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ListAdapter adapter = importSelectionView.getAdapter();
|
||||
@ -1312,7 +1457,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
boolean overwrite = false;
|
||||
|
||||
dialog.dismiss();
|
||||
new ImportAsyncTask(includeGlobals, accountUuids, overwrite, mEncryptionKey, mInputStream).execute();
|
||||
Accounts activity = (Accounts) mActivity;
|
||||
ImportAsyncTask importAsyncTask = new ImportAsyncTask(activity, includeGlobals, accountUuids, overwrite, mEncryptionKey, mInputStream);
|
||||
activity.mAsyncTask = importAsyncTask;
|
||||
importAsyncTask.execute();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel_action,
|
||||
@ -1326,11 +1474,6 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
} else {
|
||||
//TODO: make the importer return an error code; translate that error code to a localized string here
|
||||
showDialog(Accounts.this, R.string.settings_import_failed_header,
|
||||
Accounts.this.getString(R.string.settings_import_failure, "unknown", "Something went wrong"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
101
src/com/fsck/k9/activity/misc/ExtendedAsyncTask.java
Normal file
101
src/com/fsck/k9/activity/misc/ExtendedAsyncTask.java
Normal file
@ -0,0 +1,101 @@
|
||||
package com.fsck.k9.activity.misc;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
/**
|
||||
* Extends {@link AsyncTask} with methods to attach and detach an {@link Activity}.
|
||||
*
|
||||
* <p>
|
||||
* This is necessary to properly handle configuration changes that will restart an activity.
|
||||
* </p><p>
|
||||
* <strong>Note:</strong>
|
||||
* Implementing classes need to make sure they have no reference to the {@code Activity} instance
|
||||
* that created the instance of that class. So if it's implemented as inner class, it needs to be
|
||||
* {@code static}.
|
||||
* </p>
|
||||
*
|
||||
* @param <Params>
|
||||
* see {@link AsyncTask}
|
||||
* @param <Progress>
|
||||
* see {@link AsyncTask}
|
||||
* @param <Result>
|
||||
* see {@link AsyncTask}
|
||||
*
|
||||
* @see #attach(Activity)
|
||||
* @see #detach()
|
||||
*/
|
||||
public abstract class ExtendedAsyncTask<Params, Progress, Result>
|
||||
extends AsyncTask<Params, Progress, Result> {
|
||||
protected Activity mActivity;
|
||||
protected Context mContext;
|
||||
protected ProgressDialog mProgressDialog;
|
||||
|
||||
protected ExtendedAsyncTask(Activity activity) {
|
||||
mActivity = activity;
|
||||
mContext = activity.getApplicationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect this {@link AsyncTask} to a new {@link Activity} instance after the activity
|
||||
* was restarted due to a configuration change.
|
||||
*
|
||||
* <p>
|
||||
* This also creates a new progress dialog that is bound to the new activity.
|
||||
* </p>
|
||||
*
|
||||
* @param activity
|
||||
* The new {@code Activity} instance. Never {@code null}.
|
||||
*/
|
||||
public void attach(Activity activity) {
|
||||
mActivity = activity;
|
||||
showProgressDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach this {@link AsyncTask} from the {@link Activity} it was bound to.
|
||||
*
|
||||
* <p>
|
||||
* This needs to be called when the current activity is being destroyed during an activity
|
||||
* restart due to a configuration change.<br/>
|
||||
* We also have to destroy the progress dialog because it's bound to the activity that's
|
||||
* being destroyed.
|
||||
* </p>
|
||||
*
|
||||
* @see Activity#onRetainNonConfigurationInstance()
|
||||
*/
|
||||
public void detach() {
|
||||
removeProgressDialog();
|
||||
mActivity = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ProgressDialog} that is shown while the background thread is running.
|
||||
*
|
||||
* <p>
|
||||
* This needs to store a {@code ProgressDialog} instance in {@link #mProgressDialog} or
|
||||
* override {@link #removeProgressDialog()}.
|
||||
* </p>
|
||||
*/
|
||||
protected abstract void showProgressDialog();
|
||||
|
||||
protected void removeProgressDialog() {
|
||||
mProgressDialog.dismiss();
|
||||
mProgressDialog = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This default implementation only creates a progress dialog.
|
||||
*
|
||||
* <p>
|
||||
* <strong>Important:</strong>
|
||||
* Be sure to call {@link #removeProgressDialog()} in {@link AsyncTask#onPostExecute(Object)}.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
showProgressDialog();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user