+ * 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. + *
+ */ + 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,34 +335,48 @@ 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 { - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - requestWindowFeature(Window.FEATURE_PROGRESS); - - setContentView(R.layout.accounts); - ListView listView = getListView(); - listView.setOnItemClickListener(this); - listView.setItemsCanFocus(false); - listView.setEmptyView(findViewById(R.id.empty)); - findViewById(R.id.next).setOnClickListener(this); - registerForContextMenu(listView); - - if (icicle != null && icicle.containsKey(SELECTED_CONTEXT_ACCOUNT)) { - String accountUuid = icicle.getString("selectedContextAccount"); - mSelectedContextAccount = Preferences.getPreferences(this).getAccount(accountUuid); - } - - restoreAccountStats(icicle); + return; } + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + requestWindowFeature(Window.FEATURE_PROGRESS); + setContentView(R.layout.accounts); + ListView listView = getListView(); + listView.setOnItemClickListener(this); + listView.setItemsCanFocus(false); + listView.setEmptyView(findViewById(R.id.empty)); + findViewById(R.id.next).setOnClickListener(this); + registerForContextMenu(listView); + if (icicle != null && icicle.containsKey(SELECTED_CONTEXT_ACCOUNT)) { + String accountUuid = icicle.getString("selectedContextAccount"); + mSelectedContextAccount = Preferences.getPreferences(this).getAccount(accountUuid); + } + + 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+ * This is necessary to properly handle configuration changes that will restart an activity. + *
+ * Note: + * 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}. + *
+ * + * @param