From 06415a635f5f01d8e1620b29f44d68dc4dfdf435 Mon Sep 17 00:00:00 2001 From: Tony Mantler Date: Wed, 25 Jun 2014 13:23:21 -0700 Subject: [PATCH] Unify settings activity b/9566150 - Install PublicPreferenceActivity - Create AccountServerSettingsActivity to contain all the crazy logic we don't want in AccountSettings - Add restoreAccountWithAddress to Account - Refactor almost everything out of AccountSettings - Tidy the QuickResponse fragment - Majorly refactor the AccountSettingsFragment to decouple it from the activity, and make it save settings immediately instead of when pausing - Move login warning dialog to AccountSettingsFragment - Tweak HeadlessAccountSettingsLoader Change-Id: Ie69181d968b9c3e5940cfef9690b1f5c70e21aa8 --- AndroidManifest.xml | 34 +- .../android/emailcommon/provider/Account.java | 18 + res/layout/account_server_settings.xml | 22 + .../setup/AccountServerSettingsActivity.java | 261 +++++++ .../email/activity/setup/AccountSettings.java | 738 +----------------- ...untSettingsEditQuickResponsesFragment.java | 21 +- .../setup/AccountSettingsFragment.java | 548 +++++++------ .../setup/HeadlessAccountSettingsLoader.java | 17 +- .../activity/setup/SetupDataFragment.java | 10 - .../activity/setup/AccountSettingsTests.java | 6 +- .../setup/AccountSetupIncomingTests.java | 4 +- .../setup/AccountSetupOptionsTests.java | 4 +- .../setup/AccountSetupOutgoingTests.java | 4 +- 13 files changed, 677 insertions(+), 1010 deletions(-) create mode 100644 res/layout/account_server_settings.xml create mode 100644 src/com/android/email/activity/setup/AccountServerSettingsActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index caac4d492..09e3d1f2a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -272,30 +272,40 @@ android:name="android.intent.category.DEFAULT" /> - + android:exported="false" > + + + + + + + + + - - - - - + + + + + + + diff --git a/src/com/android/email/activity/setup/AccountServerSettingsActivity.java b/src/com/android/email/activity/setup/AccountServerSettingsActivity.java new file mode 100644 index 000000000..fffa15a7f --- /dev/null +++ b/src/com/android/email/activity/setup/AccountServerSettingsActivity.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.email.activity.setup; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; + +import com.android.email.R; +import com.android.emailcommon.provider.Account; +import com.android.mail.utils.LogUtils; + +public class AccountServerSettingsActivity extends AccountSetupActivity implements + SecurityRequiredDialogFragment.Callback, CheckSettingsErrorDialogFragment.Callback, + AccountCheckSettingsFragment.Callback, AccountServerBaseFragment.Callback, + CheckSettingsProgressDialogFragment.Callback { + + /** + * {@link com.android.emailcommon.provider.Account} + */ + private static final String EXTRA_ACCOUNT = "account"; + /** + * Incoming or Outgoing settings? + */ + private static final String EXTRA_WHICH_SETTINGS = "whichSettings"; + private static final String INCOMING_SETTINGS = "incoming"; + private static final String OUTGOING_SETTINGS = "outgoing"; + + private AccountServerBaseFragment mAccountServerFragment; + + public static Intent getIntentForIncoming(final Context context, final Account account) { + final Intent intent = new Intent(context, AccountServerSettingsActivity.class); + intent.putExtra(EXTRA_ACCOUNT, account); + intent.putExtra(EXTRA_WHICH_SETTINGS, INCOMING_SETTINGS); + return intent; + } + + public static Intent getIntentForOutgoing(final Context context, final Account account) { + final Intent intent = new Intent(context, AccountServerSettingsActivity.class); + intent.putExtra(EXTRA_ACCOUNT, account); + intent.putExtra(EXTRA_WHICH_SETTINGS, OUTGOING_SETTINGS); + return intent; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mSetupData.setFlowMode(SetupDataFragment.FLOW_MODE_EDIT); + + // TODO: this activity is ugly as sin + setContentView(R.layout.account_server_settings); + + if (savedInstanceState == null) { + final Account account = getIntent().getParcelableExtra(EXTRA_ACCOUNT); + if (account == null) { + throw new IllegalArgumentException("No account present in intent"); + } + mSetupData.setAccount(account); + final String whichSettings = getIntent().getStringExtra(EXTRA_WHICH_SETTINGS); + final AccountServerBaseFragment f; + if (OUTGOING_SETTINGS.equals(whichSettings)) { + f = AccountSetupOutgoingFragment.newInstance(true); + } else { + f = AccountSetupIncomingFragment.newInstance(true); + } + getFragmentManager().beginTransaction() + .add(R.id.account_server_settings_container, f) + .commit(); + } + } + + @Override + public void onAttachFragment(Fragment fragment) { + super.onAttachFragment(fragment); + if (fragment instanceof AccountServerBaseFragment) { + mAccountServerFragment = (AccountServerBaseFragment) fragment; + } + } + + public AccountServerBaseFragment getAccountServerFragment() { + return mAccountServerFragment; + } + + private void forceBack() { + super.onBackPressed(); + } + + /** + * Any time we exit via this pathway we put up the exit-save-changes dialog. + */ + @Override + public void onBackPressed() { + final AccountServerBaseFragment accountServerFragment = getAccountServerFragment(); + if (accountServerFragment != null) { + if (accountServerFragment.haveSettingsChanged()) { + UnsavedChangesDialogFragment dialogFragment = + UnsavedChangesDialogFragment.newInstanceForBack(); + dialogFragment.show(getFragmentManager(), UnsavedChangesDialogFragment.TAG); + return; // Prevent "back" from being handled + } + } + super.onBackPressed(); + } + + @Override + public void onNextButton() {} + + /** + * Save process is done, dismiss the fragment. + */ + @Override + public void onAccountServerSaveComplete() { + super.onBackPressed(); + } + + @Override + public void onAccountServerUIComplete(int checkMode) { + final Fragment checkerDialog = CheckSettingsProgressDialogFragment.newInstance(checkMode); + final Fragment checkerFragment = AccountCheckSettingsFragment.newInstance(checkMode); + getFragmentManager().beginTransaction() + .add(checkerDialog, CheckSettingsProgressDialogFragment.TAG) + .add(checkerFragment, AccountCheckSettingsFragment.TAG) + .commit(); + } + + @Override + public void onCheckSettingsProgressDialogCancel() { + dismissCheckSettingsFragment(); + } + + /** + * After verifying a new server configuration as OK, we return here and continue. This kicks + * off the save process. + */ + @Override + public void onCheckSettingsComplete() { + dismissCheckSettingsFragment(); + final AccountServerBaseFragment f = getAccountServerFragment(); + if (f != null) { + f.saveSettings(); + } + } + + @Override + public void onCheckSettingsSecurityRequired(String hostName) { + dismissCheckSettingsFragment(); + SecurityRequiredDialogFragment.newInstance(hostName) + .show(getFragmentManager(), SecurityRequiredDialogFragment.TAG); + } + + @Override + public void onCheckSettingsError(int reason, String message) { + dismissCheckSettingsFragment(); + CheckSettingsErrorDialogFragment.newInstance(reason, message) + .show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG); + } + + @Override + public void onCheckSettingsAutoDiscoverComplete(int result) { + throw new IllegalStateException(); + } + + private void dismissCheckSettingsFragment() { + final Fragment f = + getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG); + final Fragment d = + getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG); + getFragmentManager().beginTransaction() + .remove(f) + .remove(d) + .commit(); + } + + @Override + public void onSecurityRequiredDialogResult(boolean ok) { + if (ok) { + final AccountServerBaseFragment f = getAccountServerFragment(); + if (f != null) { + f.saveSettings(); + } + } + // else just stay here + } + + @Override + public void onCheckSettingsErrorDialogEditSettings() { + // Just stay here + } + + @Override + public void onCheckSettingsErrorDialogEditCertificate() { + final AccountServerBaseFragment f = getAccountServerFragment(); + if (f instanceof AccountSetupIncomingFragment) { + AccountSetupIncomingFragment asif = (AccountSetupIncomingFragment) f; + asif.onCertificateRequested(); + } else { + LogUtils.wtf(LogUtils.TAG, "Tried to change cert on non-incoming screen?"); + } + } + + /** + * Dialog fragment to show "exit with unsaved changes?" dialog + */ + public static class UnsavedChangesDialogFragment extends DialogFragment { + final static String TAG = "UnsavedChangesDialogFragment"; + + /** + * Creates a save changes dialog when the user navigates "back". + * {@link #onBackPressed()} defines in which case this may be triggered. + */ + public static UnsavedChangesDialogFragment newInstanceForBack() { + return new UnsavedChangesDialogFragment(); + } + + // Force usage of newInstance() + public UnsavedChangesDialogFragment() {} + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final AccountServerSettingsActivity activity = + (AccountServerSettingsActivity) getActivity(); + + return new AlertDialog.Builder(activity) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setTitle(android.R.string.dialog_alert_title) + .setMessage(R.string.account_settings_exit_server_settings) + .setPositiveButton( + android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + activity.forceBack(); + dismiss(); + } + }) + .setNegativeButton( + activity.getString(android.R.string.cancel), null) + .create(); + } + } +} diff --git a/src/com/android/email/activity/setup/AccountSettings.java b/src/com/android/email/activity/setup/AccountSettings.java index 6557c2393..e68c0bfd9 100644 --- a/src/com/android/email/activity/setup/AccountSettings.java +++ b/src/com/android/email/activity/setup/AccountSettings.java @@ -17,41 +17,21 @@ package com.android.email.activity.setup; import android.app.ActionBar; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; import android.app.Fragment; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.database.Cursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.method.LinkMovementMethod; -import android.text.util.Linkify; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; -import android.widget.TextView; import com.android.email.R; -import com.android.email.provider.EmailProvider; -import com.android.emailcommon.Logging; -import com.android.emailcommon.provider.Account; -import com.android.emailcommon.provider.EmailContent.AccountColumns; import com.android.emailcommon.utility.IntentUtilities; -import com.android.emailcommon.utility.Utility; import com.android.mail.providers.Folder; import com.android.mail.providers.UIProvider.EditSettingsExtras; import com.android.mail.ui.settings.MailPreferenceActivity; -import com.android.mail.utils.LogUtils; import com.android.mail.utils.Utils; -import com.google.common.annotations.VisibleForTesting; import java.util.List; @@ -60,20 +40,11 @@ import java.util.List; * * This activity uses the following fragments: * AccountSettingsFragment - * Account{Incoming/Outgoing}Fragment - * AccountCheckSettingsFragment * GeneralPreferences * DebugFragment * - * TODO: Delete account - on single-pane view (phone UX) the account list doesn't update properly - * TODO: Handle dynamic changes to the account list (exit if necessary). It probably makes - * sense to use a loader for the accounts list, because it would provide better support for - * dealing with accounts being added/deleted and triggering the header reload. */ -public class AccountSettings extends MailPreferenceActivity implements - SetupDataFragment.SetupDataContainer, SecurityRequiredDialogFragment.Callback, - CheckSettingsErrorDialogFragment.Callback, AccountCheckSettingsFragment.Callback, - AccountServerBaseFragment.Callback, CheckSettingsProgressDialogFragment.Callback { +public class AccountSettings extends MailPreferenceActivity { /* * Intent to open account settings for account=1 adb shell am start -a android.intent.action.EDIT \ @@ -85,20 +56,13 @@ public class AccountSettings extends MailPreferenceActivity implements private static final String EXTRA_LOGIN_WARNING_FOR_ACCOUNT = "AccountSettings.for_account"; private static final String EXTRA_LOGIN_WARNING_REASON_FOR_ACCOUNT = "AccountSettings.for_account_reason"; - private static final String EXTRA_TITLE = "AccountSettings.title"; // STOPSHIP: Do not ship with the debug menu allowed. private static final boolean DEBUG_MENU_ALLOWED = false; public static final String EXTRA_NO_ACCOUNTS = "AccountSettings.no_account"; - public static final String EXTRA_ACCOUNT = "AccountSettings.account"; // Intent extras for launch directly from system account manager // NOTE: This string must match the one in res/xml/account_preferences.xml private static String INTENT_ACCOUNT_MANAGER_ENTRY; - // NOTE: This constant should eventually be defined in android.accounts.Constants - private static final String EXTRA_ACCOUNT_MANAGER_ACCOUNT = "account"; - - // Key for arguments bundle for QuickResponse editing - private static final String QUICK_RESPONSE_ACCOUNT_KEY = "account"; // Key codes used to open a debug settings fragment. private static final int[] SECRET_KEY_CODES = { @@ -107,35 +71,13 @@ public class AccountSettings extends MailPreferenceActivity implements }; private int mSecretKeyCodeIndex = 0; - // Support for account-by-name lookup - private static final String SELECTION_ACCOUNT_EMAIL_ADDRESS = - AccountColumns.EMAIL_ADDRESS + "=?"; - // When the user taps "Email Preferences" 10 times in a row, we'll enable the debug settings. private int mNumGeneralHeaderClicked = 0; - private long mRequestedAccountId; - private Header[] mAccountListHeaders; - private Header mAppPreferencesHeader; - private AccountSettingsFragment mAccountSettingsFragment; - private AccountServerBaseFragment mAccountServerFragment; - private long mDeletingAccountId = -1; private boolean mShowDebugMenu; - private List
mGeneratedHeaders; private Uri mFeedbackUri; private MenuItem mFeedbackMenuItem; - private SetupDataFragment mSetupData; - - // Async Tasks - private LoadAccountListTask mLoadAccountListTask; - private GetAccountIdFromAccountTask mGetAccountIdFromAccountTask; - private ContentObserver mAccountObserver; - - // Specific callbacks used by settings fragments - private final AccountSettingsFragmentCallback mAccountSettingsFragmentCallback - = new AccountSettingsFragmentCallback(); - /** * Create and return an intent to display (and edit) settings for a specific account, or -1 * for any/all accounts. If an account name string is provided, a warning dialog will be @@ -169,7 +111,7 @@ public class AccountSettings extends MailPreferenceActivity implements modIntent.putExtra( EXTRA_SHOW_FRAGMENT_ARGUMENTS, AccountSettingsFragment.buildArguments( - accountId, IntentUtilities.getAccountNameFromIntent(intent))); + IntentUtilities.getAccountNameFromIntent(intent))); modIntent.putExtra(EXTRA_NO_HEADERS, true); return modIntent; } @@ -198,10 +140,12 @@ public class AccountSettings extends MailPreferenceActivity implements INTENT_ACCOUNT_MANAGER_ENTRY = getString(R.string.intent_account_manager_entry); } if (INTENT_ACCOUNT_MANAGER_ENTRY.equals(i.getAction())) { - // This case occurs if we're changing account settings from Settings -> Accounts - mGetAccountIdFromAccountTask = - (GetAccountIdFromAccountTask) new GetAccountIdFromAccountTask() - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, i); + // This case occurs if we're changing account settings from Settings -> Accounts. + // We get an account object in the intent, but it's not actually useful to us since + // it's always just the first account of that type. The user can't specify which + // account they wish to view from within the settings UI, so just dump them at the + // main screen. + // android.accounts.Account acct = i.getParcelableExtra("account"); } else if (i.hasExtra(EditSettingsExtras.EXTRA_FOLDER)) { launchMailboxSettings(i); return; @@ -210,75 +154,28 @@ public class AccountSettings extends MailPreferenceActivity implements startActivity(setupIntent); finish(); return; - } else if (i.hasExtra(EXTRA_ACCOUNT)) { - final Account account = i.getParcelableExtra(EXTRA_ACCOUNT); - mSetupData = new SetupDataFragment(SetupDataFragment.FLOW_MODE_EDIT, account); } else { // Otherwise, we're called from within the Email app and look for our extras - mRequestedAccountId = IntentUtilities.getAccountIdFromIntent(i); - String loginWarningAccount = i.getStringExtra(EXTRA_LOGIN_WARNING_FOR_ACCOUNT); - String loginWarningReason = - i.getStringExtra(EXTRA_LOGIN_WARNING_REASON_FOR_ACCOUNT); - if (loginWarningAccount != null) { - // Show dialog (first time only - don't re-show on a rotation) - LoginWarningDialog dialog = - LoginWarningDialog.newInstance(loginWarningAccount, loginWarningReason); - dialog.show(getFragmentManager(), "loginwarning"); + final long accountId = IntentUtilities.getAccountIdFromIntent(i); + if (accountId != -1) { + String loginWarningAccount = i.getStringExtra(EXTRA_LOGIN_WARNING_FOR_ACCOUNT); + String loginWarningReason = + i.getStringExtra(EXTRA_LOGIN_WARNING_REASON_FOR_ACCOUNT); + final Bundle args = AccountSettingsFragment.buildArguments(accountId, + loginWarningAccount, loginWarningReason); + startPreferencePanel(AccountSettingsFragment.class.getName(), args, + 0, null, null, 0); } } - } else { - mSetupData = savedInstanceState.getParcelable(SetupDataFragment.EXTRA_SETUP_DATA); } mShowDebugMenu = i.getBooleanExtra(EXTRA_ENABLE_DEBUG, false); - final String title = i.getStringExtra(EXTRA_TITLE); - if (title != null) { - setTitle(title); - } - getActionBar().setDisplayOptions( ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP); - mAccountObserver = new ContentObserver(Utility.getMainThreadHandler()) { - @Override - public void onChange(boolean selfChange) { - updateAccounts(); - } - }; - mFeedbackUri = Utils.getValidUri(getString(R.string.email_feedback_uri)); } - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState( - outState); - // TODO: use the fragment manager to persist this - outState.putParcelable(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData); - } - - @Override - public void onResume() { - super.onResume(); - getContentResolver().registerContentObserver(Account.NOTIFIER_URI, true, mAccountObserver); - updateAccounts(); - } - - @Override - public void onPause() { - super.onPause(); - getContentResolver().unregisterContentObserver(mAccountObserver); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Utility.cancelTaskInterrupt(mLoadAccountListTask); - mLoadAccountListTask = null; - Utility.cancelTaskInterrupt(mGetAccountIdFromAccountTask); - mGetAccountIdFromAccountTask = null; - } - /** * Listen for secret sequence and, if heard, enable debug menu */ @@ -339,55 +236,10 @@ public class AccountSettings extends MailPreferenceActivity implements return true; } + @Override public boolean isValidFragment(String fragmentName) { - // We need to make sure that a fragment about to be attached is valid. This corrects - // a security vulnerability. - return (TextUtils.equals(AccountSettingsFragment.class.getName(), fragmentName) || - TextUtils.equals(GeneralPreferences.class.getName(), fragmentName) || - TextUtils.equals(AccountSetupIncomingFragment.class.getName(), fragmentName) || - TextUtils.equals(AccountSettingsEditQuickResponsesFragment.class.getName(), - fragmentName) || - TextUtils.equals(DebugFragment.class.getName(), fragmentName) || - super.isValidFragment(fragmentName)); - } - - @Override - public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, - int titleRes, int shortTitleRes) { - final Intent intent = super.onBuildStartFragmentIntent( - fragmentName, args, titleRes, shortTitleRes); - - // When opening a sub-settings page (e.g. account specific page), see if we want to modify - // the activity title. - String title = AccountSettingsFragment.getTitleFromArgs(args); - if ((titleRes == 0) && (title != null)) { - intent.putExtra(EXTRA_TITLE, title); - } - return intent; - } - - /** - * Any time we exit via this pathway, and we are showing a server settings fragment, - * we put up the exit-save-changes dialog. This will work for the following cases: - * Cancel button - * Back button - * Up arrow in application icon - * It will *not* apply in the following cases: - * Click the parent breadcrumb - need to find a hook for this - * Click in the header list (e.g. another account) - handled elsewhere - */ - @Override - public void onBackPressed() { - final AccountServerBaseFragment accountServerFragment = getAccountServerFragment(); - if (accountServerFragment != null) { - if (accountServerFragment.haveSettingsChanged()) { - UnsavedChangesDialogFragment dialogFragment = - UnsavedChangesDialogFragment.newInstanceForBack(); - dialogFragment.show(getFragmentManager(), UnsavedChangesDialogFragment.TAG); - return; // Prevent "back" from being handled - } - } - super.onBackPressed(); + // This activity is not exported, so we can allow any fragment + return true; } @@ -415,46 +267,9 @@ public class AccountSettings extends MailPreferenceActivity implements startActivity(setupIntent); } - /** - * Start the async reload of the accounts list (if the headers are being displayed) - */ - private void updateAccounts() { - if (hasHeaders()) { - Utility.cancelTaskInterrupt(mLoadAccountListTask); - mLoadAccountListTask = (LoadAccountListTask) - new LoadAccountListTask().executeOnExecutor( - AsyncTask.THREAD_POOL_EXECUTOR, mDeletingAccountId); - } - } - - /** - * Write the current header (accounts) array into the one provided by the PreferenceActivity. - * Skip any headers that match mDeletingAccountId (this is a quick-hide algorithm while a - * background thread works on deleting the account). Also sets mRequestedAccountHeader if - * we find the requested account (by id). - */ @Override - public void onBuildHeaders(List
target) { - // Always add app preferences as first header - target.clear(); - target.add(getAppPreferencesHeader()); - - // Then add zero or more account headers as necessary - if (mAccountListHeaders != null) { - final int headerCount = mAccountListHeaders.length; - for (int index = 0; index < headerCount; index++) { - Header header = mAccountListHeaders[index]; - if (header != null && header.id != HEADER_ID_UNDEFINED) { - if (header.id != mDeletingAccountId) { - target.add(header); - if (header.id == mRequestedAccountId) { - mRequestedAccountId = -1; - } - } - } - } - } - + public void onBuildExtraHeaders(List
target) { + super.onBuildExtraHeaders(target); // finally, if debug header is enabled, show it if (DEBUG_MENU_ALLOWED) { if (mShowDebugMenu) { @@ -468,92 +283,6 @@ public class AccountSettings extends MailPreferenceActivity implements target.add(debugHeader); } } - - // Save for later use (see forceSwitch) - mGeneratedHeaders = target; - } - - /** - * Generate and return the first header, for app preferences - */ - private Header getAppPreferencesHeader() { - // Set up fixed header for general settings - if (mAppPreferencesHeader == null) { - mAppPreferencesHeader = new Header(); - mAppPreferencesHeader.title = getText(R.string.header_label_general_preferences); - mAppPreferencesHeader.summary = null; - mAppPreferencesHeader.iconRes = 0; - mAppPreferencesHeader.fragment = GeneralPreferences.class.getName(); - mAppPreferencesHeader.fragmentArguments = null; - } - return mAppPreferencesHeader; - } - - /** - * This AsyncTask reads the accounts list and generates the headers. When the headers are - * ready, we'll trigger PreferenceActivity to refresh the account list with them. - * - * The array generated and stored in mAccountListHeaders may be sparse so any readers should - * check for and skip over null entries, and should not assume array length is # of accounts. - * - * TODO: Smaller projection - * TODO: Convert to Loader - * TODO: Write a test, including operation of deletingAccountId param - */ - private class LoadAccountListTask extends AsyncTask { - - @Override - protected Object[] doInBackground(Long... params) { - Header[] result = null; - Boolean deletingAccountFound = false; - final long deletingAccountId = params[0]; - - Cursor c = getContentResolver().query( - Account.CONTENT_URI, - Account.CONTENT_PROJECTION, null, null, null); - try { - int index = 0; - result = new Header[c.getCount()]; - - while (c.moveToNext()) { - final long accountId = c.getLong(Account.CONTENT_ID_COLUMN); - if (accountId == deletingAccountId) { - deletingAccountFound = true; - continue; - } - final String name = c.getString(Account.CONTENT_DISPLAY_NAME_COLUMN); - final String email = c.getString(Account.CONTENT_EMAIL_ADDRESS_COLUMN); - final Header newHeader = new Header(); - newHeader.id = accountId; - newHeader.title = name; - newHeader.summary = email; - newHeader.fragment = AccountSettingsFragment.class.getCanonicalName(); - newHeader.fragmentArguments = - AccountSettingsFragment.buildArguments(accountId, email); - - result[index++] = newHeader; - } - } finally { - if (c != null) { - c.close(); - } - } - return new Object[] { result, deletingAccountFound }; - } - - @Override - protected void onPostExecute(Object[] result) { - if (isCancelled() || result == null) return; - // Extract the results - final Header[] headers = (Header[]) result[0]; - final boolean deletingAccountFound = (Boolean) result[1]; - // report the settings - mAccountListHeaders = headers; - invalidateHeaders(); - if (!deletingAccountFound) { - mDeletingAccountId = -1; - } - } } /** @@ -564,15 +293,6 @@ public class AccountSettings extends MailPreferenceActivity implements */ @Override public void onHeaderClick(Header header, int position) { - // special case when exiting the server settings fragments - final AccountServerBaseFragment accountServerFragment = getAccountServerFragment(); - if ((accountServerFragment != null) && accountServerFragment.haveSettingsChanged()) { - UnsavedChangesDialogFragment dialogFragment = - UnsavedChangesDialogFragment.newInstanceForHeader(position); - dialogFragment.show(getFragmentManager(), UnsavedChangesDialogFragment.TAG); - return; - } - // Secret keys: Click 10x to enable debug settings if (position == 0) { mNumGeneralHeaderClicked++; @@ -587,424 +307,10 @@ public class AccountSettings extends MailPreferenceActivity implements super.onHeaderClick(header, position); } - /** - * Switch to a specific header without checking for server settings fragments as done - * in {@link #onHeaderClick(Header, int)}. Called after we interrupted a header switch - * with a dialog, and the user OK'd it. - */ - private void forceSwitchHeader(int position) { - // Ensure the UI visually shows the correct header selected - setSelection(position); - switchToHeader(mGeneratedHeaders.get(position)); - } - - /** - * Forcefully go backward in the stack. This may potentially discard unsaved settings. - */ - private void forceBack() { - super.onBackPressed(); - } - @Override public void onAttachFragment(Fragment f) { super.onAttachFragment(f); - - if (f instanceof AccountSettingsFragment) { - final AccountSettingsFragment asf = (AccountSettingsFragment) f; - mAccountSettingsFragment = asf; - asf.setCallback(mAccountSettingsFragmentCallback); - } else if (f instanceof AccountServerBaseFragment) { - mAccountServerFragment = (AccountServerBaseFragment) f; - } else { - // Possibly uninteresting fragment, such as a dialog. - return; - } // When we're changing fragments, enable/disable the add account button invalidateOptionsMenu(); } - - @VisibleForTesting - protected AccountSettingsFragment getSettingsFragment() { - return mAccountSettingsFragment != null && mAccountSettingsFragment.isAdded() ? - mAccountSettingsFragment : null; - } - - protected AccountServerBaseFragment getAccountServerFragment() { - return mAccountServerFragment != null && mAccountServerFragment.isAdded() ? - mAccountServerFragment : null; - } - - /** - * Callbacks for AccountSettingsFragment - */ - private class AccountSettingsFragmentCallback implements AccountSettingsFragment.Callback { - @Override - public void onSettingsChanged(long accountId, String preference, Object value) { - AccountSettings.this.onSettingsChanged(accountId, preference, value); - } - @Override - public void onEditQuickResponses(com.android.mail.providers.Account account) { - AccountSettings.this.onEditQuickResponses(account); - } - @Override - public void onIncomingSettings(Account account) { - AccountSettings.this.onIncomingSettings(account); - } - @Override - public void onOutgoingSettings(Account account) { - AccountSettings.this.onOutgoingSettings(account); - } - @Override - public void abandonEdit() { - finish(); - } - } - - @Override - public void onNextButton() {} - - /** - * Save process is done, dismiss the fragment. - */ - @Override - public void onAccountServerSaveComplete() { - super.onBackPressed(); - } - - @Override - public void onAccountServerUIComplete(int checkMode) { - Fragment checkerDialog = CheckSettingsProgressDialogFragment.newInstance(checkMode); - Fragment checkerFragment = AccountCheckSettingsFragment.newInstance(checkMode); - getFragmentManager().beginTransaction() - .add(checkerDialog, CheckSettingsProgressDialogFragment.TAG) - .add(checkerFragment, AccountCheckSettingsFragment.TAG) - .commit(); - } - - @Override - public void onCheckSettingsProgressDialogCancel() { - dismissCheckSettingsFragment(); - } - - /** - * After verifying a new server configuration as OK, we return here and continue. This kicks - * off the save process. - */ - @Override - public void onCheckSettingsComplete() { - dismissCheckSettingsFragment(); - final AccountServerBaseFragment f = getAccountServerFragment(); - if (f != null) { - f.saveSettings(); - } - } - - @Override - public void onCheckSettingsSecurityRequired(String hostName) { - dismissCheckSettingsFragment(); - SecurityRequiredDialogFragment.newInstance(hostName) - .show(getFragmentManager(), SecurityRequiredDialogFragment.TAG); - } - - @Override - public void onCheckSettingsError(int reason, String message) { - dismissCheckSettingsFragment(); - CheckSettingsErrorDialogFragment.newInstance(reason, message) - .show(getFragmentManager(), CheckSettingsErrorDialogFragment.TAG); - } - - @Override - public void onCheckSettingsAutoDiscoverComplete(int result) { - throw new IllegalStateException(); - } - - private void dismissCheckSettingsFragment() { - final Fragment f = - getFragmentManager().findFragmentByTag(AccountCheckSettingsFragment.TAG); - final Fragment d = - getFragmentManager().findFragmentByTag(CheckSettingsProgressDialogFragment.TAG); - getFragmentManager().beginTransaction() - .remove(f) - .remove(d) - .commit(); - } - - @Override - public void onSecurityRequiredDialogResult(boolean ok) { - if (ok) { - final AccountServerBaseFragment f = getAccountServerFragment(); - if (f != null) { - f.saveSettings(); - } - } - // else just stay here - } - - @Override - public void onCheckSettingsErrorDialogEditSettings() { - // Just stay here - } - - @Override - public void onCheckSettingsErrorDialogEditCertificate() { - final AccountServerBaseFragment f = getAccountServerFragment(); - if (f instanceof AccountSetupIncomingFragment) { - AccountSetupIncomingFragment asif = (AccountSetupIncomingFragment) f; - asif.onCertificateRequested(); - } else { - LogUtils.wtf(LogUtils.TAG, "Tried to change cert on non-incoming screen?"); - } - } - - /** - * Some of the settings have changed. Update internal state as necessary. - */ - public void onSettingsChanged(long accountId, String preference, Object value) { - if (AccountSettingsFragment.PREFERENCE_DESCRIPTION.equals(preference)) { - for (Header header : mAccountListHeaders) { - if (header.id == accountId) { - // Manually tweak the header title. We cannot rebuild the header list from - // an account cursor as the account database has not been saved yet. - header.title = value.toString(); - invalidateHeaders(); - break; - } - } - } - } - - /** - * Dispatch to edit quick responses. - */ - public void onEditQuickResponses(com.android.mail.providers.Account account) { - try { - final Bundle args = new Bundle(1); - args.putParcelable(QUICK_RESPONSE_ACCOUNT_KEY, account); - startPreferencePanel(AccountSettingsEditQuickResponsesFragment.class.getName(), args, - R.string.account_settings_edit_quick_responses_label, null, null, 0); - } catch (Exception e) { - LogUtils.d(Logging.LOG_TAG, "Error while trying to invoke edit quick responses.", e); - } - } - - /** - * Dispatch to edit incoming settings. - */ - public void onIncomingSettings(Account account) { - try { - mSetupData = new SetupDataFragment(SetupDataFragment.FLOW_MODE_EDIT, account); - final Fragment f = AccountSetupIncomingFragment.newInstance(true); - // Use startPreferenceFragment here because we need to keep this activity instance - startPreferenceFragment(f, true); - } catch (Exception e) { - LogUtils.d(Logging.LOG_TAG, "Error while trying to invoke store settings.", e); - } - } - - /** - * Dispatch to edit outgoing settings. - */ - public void onOutgoingSettings(Account account) { - try { - mSetupData = new SetupDataFragment(SetupDataFragment.FLOW_MODE_EDIT, account); - final Fragment f = AccountSetupOutgoingFragment.newInstance(true); - // Use startPreferenceFragment here because we need to keep this activity instance - startPreferenceFragment(f, true); - } catch (Exception e) { - LogUtils.d(Logging.LOG_TAG, "Error while trying to invoke sender settings.", e); - } - } - - /** - * Delete the selected account - */ - public void deleteAccount(final Account account) { - // Kick off the work to actually delete the account - new Thread(new Runnable() { - @Override - public void run() { - final Uri uri = EmailProvider.uiUri("uiaccount", account.mId); - getContentResolver().delete(uri, null, null); - }}).start(); - - // TODO: Remove ui glue for unified - // Then update the UI as appropriate: - // If single pane, return to the header list. If multi, rebuild header list - if (onIsMultiPane()) { - final Header prefsHeader = getAppPreferencesHeader(); - this.switchToHeader(prefsHeader.fragment, prefsHeader.fragmentArguments); - mDeletingAccountId = account.mId; - updateAccounts(); - } else { - // We should only be calling this while showing AccountSettingsFragment, - // so a finish() should bring us back to headers. No point hiding the deleted account. - finish(); - } - } - - /** - * This AsyncTask looks up an account based on its email address (which is what we get from - * the Account Manager). When the account id is determined, we refresh the header list, - * which will select the preferences for that account. - */ - private class GetAccountIdFromAccountTask extends AsyncTask { - - @Override - protected Long doInBackground(Intent... params) { - final Intent intent = params[0]; - android.accounts.Account acct = - intent.getParcelableExtra(EXTRA_ACCOUNT_MANAGER_ACCOUNT); - return Utility.getFirstRowLong(AccountSettings.this, Account.CONTENT_URI, - Account.ID_PROJECTION, SELECTION_ACCOUNT_EMAIL_ADDRESS, - new String[] {acct.name}, null, Account.ID_PROJECTION_COLUMN, -1L); - } - - @Override - protected void onPostExecute(Long accountId) { - if (accountId != -1 && !isCancelled()) { - mRequestedAccountId = accountId; - invalidateHeaders(); - } - } - } - - /** - * Dialog fragment to show "exit with unsaved changes?" dialog - */ - public static class UnsavedChangesDialogFragment extends DialogFragment { - final static String TAG = "UnsavedChangesDialogFragment"; - - // Argument bundle keys - private final static String BUNDLE_KEY_HEADER = "UnsavedChangesDialogFragment.Header"; - private final static String BUNDLE_KEY_BACK = "UnsavedChangesDialogFragment.Back"; - - /** - * Creates a save changes dialog when the user selects a new header - * @param position The new header index to make active if the user accepts the dialog. This - * must be a valid header index although there is no error checking. - */ - public static UnsavedChangesDialogFragment newInstanceForHeader(int position) { - final UnsavedChangesDialogFragment f = new UnsavedChangesDialogFragment(); - final Bundle b = new Bundle(1); - b.putInt(BUNDLE_KEY_HEADER, position); - f.setArguments(b); - return f; - } - - /** - * Creates a save changes dialog when the user navigates "back". - * {@link #onBackPressed()} defines in which case this may be triggered. - */ - public static UnsavedChangesDialogFragment newInstanceForBack() { - final UnsavedChangesDialogFragment f = new UnsavedChangesDialogFragment(); - final Bundle b = new Bundle(1); - b.putBoolean(BUNDLE_KEY_BACK, true); - f.setArguments(b); - return f; - } - - // Force usage of newInstance() - public UnsavedChangesDialogFragment() {} - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final AccountSettings activity = (AccountSettings) getActivity(); - final int position = getArguments().getInt(BUNDLE_KEY_HEADER); - final boolean isBack = getArguments().getBoolean(BUNDLE_KEY_BACK); - - return new AlertDialog.Builder(activity) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setTitle(android.R.string.dialog_alert_title) - .setMessage(R.string.account_settings_exit_server_settings) - .setPositiveButton( - android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (isBack) { - activity.forceBack(); - } else { - activity.forceSwitchHeader(position); - } - dismiss(); - } - }) - .setNegativeButton( - activity.getString(android.R.string.cancel), null) - .create(); - } - } - - /** - * Dialog briefly shown in some cases, to indicate the user that login failed. If the user - * clicks OK, we simply dismiss the dialog, leaving the user in the account settings for - * that account; If the user clicks "cancel", we exit account settings. - */ - public static class LoginWarningDialog extends DialogFragment - implements DialogInterface.OnClickListener { - private static final String BUNDLE_KEY_ACCOUNT_NAME = "account_name"; - private String mReason; - - // Public no-args constructor needed for fragment re-instantiation - public LoginWarningDialog() {} - - /** - * Create a new dialog. - */ - public static LoginWarningDialog newInstance(String accountName, String reason) { - final LoginWarningDialog dialog = new LoginWarningDialog(); - final Bundle b = new Bundle(1); - b.putString(BUNDLE_KEY_ACCOUNT_NAME, accountName); - dialog.setArguments(b); - dialog.mReason = reason; - return dialog; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final String accountName = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME); - - final Context context = getActivity(); - final Resources res = context.getResources(); - final AlertDialog.Builder b = new AlertDialog.Builder(context); - b.setTitle(R.string.account_settings_login_dialog_title); - b.setIconAttribute(android.R.attr.alertDialogIcon); - if (mReason != null) { - final TextView message = new TextView(context); - final String alert = res.getString( - R.string.account_settings_login_dialog_reason_fmt, accountName, mReason); - SpannableString spannableAlertString = new SpannableString(alert); - Linkify.addLinks(spannableAlertString, Linkify.WEB_URLS); - message.setText(spannableAlertString); - // There must be a better way than specifying size/padding this way - // It does work and look right, though - final int textSize = res.getDimensionPixelSize(R.dimen.dialog_text_size); - message.setTextSize(textSize); - final int paddingLeft = res.getDimensionPixelSize(R.dimen.dialog_padding_left); - final int paddingOther = res.getDimensionPixelSize(R.dimen.dialog_padding_other); - message.setPadding(paddingLeft, paddingOther, paddingOther, paddingOther); - message.setMovementMethod(LinkMovementMethod.getInstance()); - b.setView(message); - } else { - b.setMessage(res.getString(R.string.account_settings_login_dialog_content_fmt, - accountName)); - } - b.setPositiveButton(android.R.string.ok, this); - b.setNegativeButton(android.R.string.cancel, this); - return b.create(); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - dismiss(); - if (which == DialogInterface.BUTTON_NEGATIVE) { - getActivity().finish(); - } - } - } - - @Override - public SetupDataFragment getSetupData() { - return mSetupData; - } } diff --git a/src/com/android/email/activity/setup/AccountSettingsEditQuickResponsesFragment.java b/src/com/android/email/activity/setup/AccountSettingsEditQuickResponsesFragment.java index 873f673e0..3a782caaa 100644 --- a/src/com/android/email/activity/setup/AccountSettingsEditQuickResponsesFragment.java +++ b/src/com/android/email/activity/setup/AccountSettingsEditQuickResponsesFragment.java @@ -55,12 +55,17 @@ import android.widget.TextView; public class AccountSettingsEditQuickResponsesFragment extends Fragment { private Account mAccount; - private static final String BUNDLE_KEY_ACTIVITY_TITLE - = "AccountSettingsEditQuickResponsesFragment.title"; + private static final String ARG_ACCOUNT = "account"; // Public no-args constructor needed for fragment re-instantiation public AccountSettingsEditQuickResponsesFragment() {} + public static Bundle createArgs(final Account account) { + final Bundle b = new Bundle(1); + b.putParcelable(ARG_ACCOUNT, account); + return b; + } + @Override public void onAttach(Activity activity) { super.onAttach(activity); @@ -69,11 +74,6 @@ public class AccountSettingsEditQuickResponsesFragment extends Fragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - // startPreferencePanel launches this fragment with the right title initially, but - // if the device is rotated we must set the title ourselves - if (savedInstanceState != null) { - getActivity().setTitle(savedInstanceState.getString(BUNDLE_KEY_ACTIVITY_TITLE)); - } final SimpleCursorAdapter adapter = new SimpleCursorAdapter(getActivity(), R.layout.quick_response_item, null, @@ -103,17 +103,12 @@ public class AccountSettingsEditQuickResponsesFragment extends Fragment { }); } - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putString(BUNDLE_KEY_ACTIVITY_TITLE, (String) getActivity().getTitle()); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); - mAccount = args.getParcelable("account"); + mAccount = args.getParcelable(ARG_ACCOUNT); setHasOptionsMenu(true); } diff --git a/src/com/android/email/activity/setup/AccountSettingsFragment.java b/src/com/android/email/activity/setup/AccountSettingsFragment.java index 8d1b4bbcc..69919fab0 100644 --- a/src/com/android/email/activity/setup/AccountSettingsFragment.java +++ b/src/com/android/email/activity/setup/AccountSettingsFragment.java @@ -17,8 +17,16 @@ package com.android.email.activity.setup; import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; import android.app.LoaderManager; -import android.content.*; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; import android.media.Ringtone; @@ -30,15 +38,19 @@ import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; +import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceFragment; import android.provider.CalendarContract; import android.provider.ContactsContract; import android.provider.Settings; +import android.text.SpannableString; import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.text.util.Linkify; import android.view.Menu; import android.view.MenuInflater; +import android.widget.TextView; import com.android.email.R; import com.android.email.SecurityPolicy; @@ -47,9 +59,9 @@ import com.android.email.provider.FolderPickerActivity; import com.android.email.service.EmailServiceUtils; import com.android.email.service.EmailServiceUtils.EmailServiceInfo; import com.android.email2.ui.MailActivityEmail; -import com.android.emailcommon.Logging; import com.android.emailcommon.provider.Account; import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.AccountColumns; import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.provider.Policy; import com.android.mail.preferences.AccountPreferences; @@ -57,7 +69,9 @@ import com.android.mail.preferences.FolderPreferences; import com.android.mail.providers.Folder; import com.android.mail.providers.UIProvider; import com.android.mail.ui.MailAsyncTaskLoader; +import com.android.mail.ui.settings.MailAccountPrefsFragment; import com.android.mail.ui.settings.SettingsUtils; +import com.android.mail.utils.ContentProviderTask.UpdateTask; import com.android.mail.utils.LogUtils; import com.android.mail.utils.NotificationUtils; @@ -72,18 +86,23 @@ import java.util.Map; * TODO: Can we defer calling addPreferencesFromResource() until after we load the account? This * could reduce flicker. */ -public class AccountSettingsFragment extends PreferenceFragment +public class AccountSettingsFragment extends MailAccountPrefsFragment implements Preference.OnPreferenceChangeListener { - // Keys used for arguments bundle - private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId"; - private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email"; + private static final String ARG_ACCOUNT_ID = "account_id"; + private static final String ARG_LOGIN_WARNING_FOR_ACCOUNT = "warning_for_account"; + private static final String ARG_LOGIN_WARNING_REASON_FOR_ACCOUNT = "warning_for_account_reason"; + public static final String PREFERENCE_DESCRIPTION = "account_description"; private static final String PREFERENCE_NAME = "account_name"; private static final String PREFERENCE_SIGNATURE = "account_signature"; private static final String PREFERENCE_QUICK_RESPONSES = "account_quick_responses"; private static final String PREFERENCE_FREQUENCY = "account_check_frequency"; + private static final String PREFERENCE_SYNC_WINDOW = "account_sync_window"; + private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email"; + private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts"; + private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar"; private static final String PREFERENCE_BACKGROUND_ATTACHMENTS = "account_background_attachments"; private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage"; @@ -97,9 +116,6 @@ public class AccountSettingsFragment extends PreferenceFragment private static final String PREFERENCE_POLICIES_RETRY_ACCOUNT = "policies_retry_account"; private static final String PREFERENCE_INCOMING = "incoming"; private static final String PREFERENCE_OUTGOING = "outgoing"; - private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts"; - private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar"; - private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email"; private static final String PREFERENCE_SYSTEM_FOLDERS = "system_folders"; private static final String PREFERENCE_SYSTEM_FOLDERS_TRASH = "system_folders_trash"; @@ -116,19 +132,14 @@ public class AccountSettingsFragment extends PreferenceFragment private EditTextPreference mAccountSignature; private ListPreference mCheckFrequency; private ListPreference mSyncWindow; - private CheckBoxPreference mAccountBackgroundAttachments; private CheckBoxPreference mInboxVibrate; private Preference mInboxRingtone; - private CheckBoxPreference mSyncContacts; - private CheckBoxPreference mSyncCalendar; - private CheckBoxPreference mSyncEmail; private Context mContext; private Account mAccount; private com.android.mail.providers.Account mUiAccount; - private Callback mCallback = EmptyCallback.INSTANCE; - private boolean mSaveOnExit; + private EmailServiceInfo mServiceInfo; private Ringtone mRingtone; @@ -138,41 +149,29 @@ public class AccountSettingsFragment extends PreferenceFragment */ private FolderPreferences mInboxFolderPreferences; - // The ID of the account being edited - private long mAccountId; + // The email of the account being edited + private String mAccountEmail; /** - * Callback interface that owning activities must provide + * If launching with an email address, use this method to build the arguments. */ - public interface Callback { - public void onSettingsChanged(long accountId, String preference, Object value); - public void onEditQuickResponses(com.android.mail.providers.Account account); - public void onIncomingSettings(Account account); - public void onOutgoingSettings(Account account); - public void abandonEdit(); - } - - private static class EmptyCallback implements Callback { - public static final Callback INSTANCE = new EmptyCallback(); - @Override public void onSettingsChanged(long accountId, String preference, Object value) {} - @Override public void onEditQuickResponses(com.android.mail.providers.Account account) {} - @Override public void onIncomingSettings(Account account) {} - @Override public void onOutgoingSettings(Account account) {} - @Override public void abandonEdit() {} - } - - /** - * If launching with an arguments bundle, use this method to build the arguments. - */ - public static Bundle buildArguments(long accountId, String email) { - Bundle b = new Bundle(); - b.putLong(BUNDLE_KEY_ACCOUNT_ID, accountId); - b.putString(BUNDLE_KEY_ACCOUNT_EMAIL, email); + public static Bundle buildArguments(final String email) { + final Bundle b = new Bundle(1); + b.putString(ARG_ACCOUNT_EMAIL, email); return b; } - public static String getTitleFromArgs(Bundle args) { - return (args == null) ? null : args.getString(BUNDLE_KEY_ACCOUNT_EMAIL); + /** + * When launching for the login warning, we don't have the account email address, so we use the + * account ID, along with a few strings explaining the warning. + */ + public static Bundle buildArguments(final long accountId, final String warningAccount, + final String warningReason) { + final Bundle b = new Bundle(3); + b.putLong(ARG_ACCOUNT_ID, accountId); + b.putString(ARG_LOGIN_WARNING_FOR_ACCOUNT, warningAccount); + b.putString(ARG_LOGIN_WARNING_REASON_FOR_ACCOUNT, warningReason); + return b; } @Override @@ -198,7 +197,7 @@ public class AccountSettingsFragment extends PreferenceFragment // If not, activity must call startLoadingAccount() directly Bundle b = getArguments(); if (b != null) { - mAccountId = b.getLong(BUNDLE_KEY_ACCOUNT_ID, -1); + mAccountEmail = b.getString(ARG_ACCOUNT_EMAIL); } if (savedInstanceState != null) { // We won't know what the correct set of sync interval values and strings are until @@ -233,15 +232,25 @@ public class AccountSettingsFragment extends PreferenceFragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final Bundle args = new Bundle(1); - args.putLong(AccountLoaderCallbacks.ARG_ACCOUNT_ID, mAccountId); + if (!TextUtils.isEmpty(mAccountEmail)) { + args.putString(AccountLoaderCallbacks.ARG_ACCOUNT_EMAIL, mAccountEmail); + } else { + args.putLong(AccountLoaderCallbacks.ARG_ACCOUNT_ID, + getArguments().getLong(ARG_ACCOUNT_ID, -1)); + } getLoaderManager().initLoader(0, args, new AccountLoaderCallbacks(getActivity())); - } - @Override - public void onPause() { - super.onPause(); - if (mSaveOnExit) { - saveSettings(); + if (savedInstanceState == null) { + final String loginWarningAccount = + getArguments().getString(ARG_LOGIN_WARNING_FOR_ACCOUNT); + final String loginWarningReason = + getArguments().getString(ARG_LOGIN_WARNING_REASON_FOR_ACCOUNT); + if (loginWarningAccount != null) { + // Show dialog (first time only - don't re-show on a rotation) + LoginWarningDialog dialog = + LoginWarningDialog.newInstance(loginWarningAccount, loginWarningReason); + dialog.show(getFragmentManager(), "loginwarning"); + } } } @@ -287,9 +296,10 @@ public class AccountSettingsFragment extends PreferenceFragment * @return True to update the state of the Preference with the new value */ @Override - public boolean onPreferenceChange(Preference preference, Object newValue){ + public boolean onPreferenceChange(Preference preference, Object newValue) { // Can't use a switch here. Falling back to a giant conditional. final String key = preference.getKey(); + final ContentValues cv = new ContentValues(1); if (key.equals(PREFERENCE_DESCRIPTION)){ String summary = newValue.toString().trim(); if (TextUtils.isEmpty(summary)) { @@ -297,15 +307,14 @@ public class AccountSettingsFragment extends PreferenceFragment } mAccountDescription.setSummary(summary); mAccountDescription.setText(summary); - preferenceChanged(PREFERENCE_DESCRIPTION, summary); - return false; - } else if (key.equals(PREFERENCE_FREQUENCY)) { - final String summary = newValue.toString(); - final int index = mCheckFrequency.findIndexOfValue(summary); - mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]); - mCheckFrequency.setValue(summary); - preferenceChanged(PREFERENCE_FREQUENCY, newValue); - return false; + cv.put(AccountColumns.DISPLAY_NAME, summary); + } else if (key.equals(PREFERENCE_NAME)) { + final String summary = newValue.toString().trim(); + if (!TextUtils.isEmpty(summary)) { + mAccountName.setSummary(summary); + mAccountName.setText(summary); + cv.put(AccountColumns.SENDER_NAME, summary); + } } else if (key.equals(PREFERENCE_SIGNATURE)) { // Clean up signature if it's only whitespace (which is easy to do on a // soft keyboard) but leave whitespace in place otherwise, to give the user @@ -317,31 +326,87 @@ public class AccountSettingsFragment extends PreferenceFragment mAccountSignature.setText(signature); SettingsUtils.updatePreferenceSummary(mAccountSignature, signature, R.string.preferences_signature_summary_not_set); - preferenceChanged(PREFERENCE_SIGNATURE, signature); - return false; - } else if (key.equals(PREFERENCE_NAME)) { - final String summary = newValue.toString().trim(); - if (!TextUtils.isEmpty(summary)) { - mAccountName.setSummary(summary); - mAccountName.setText(summary); - preferenceChanged(PREFERENCE_NAME, summary); + cv.put(AccountColumns.SIGNATURE, signature); + } else if (key.equals(PREFERENCE_FREQUENCY)) { + final String summary = newValue.toString(); + final int index = mCheckFrequency.findIndexOfValue(summary); + mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]); + mCheckFrequency.setValue(summary); + if (mServiceInfo.syncContacts || mServiceInfo.syncCalendar) { + // This account allows syncing of contacts and/or calendar, so we will always have + // separate preferences to enable or disable syncing of email, contacts, and + // calendar. + // The "sync frequency" preference really just needs to control the frequency value + // in our database. + cv.put(AccountColumns.SYNC_INTERVAL, Integer.parseInt(summary)); + } else { + // This account only syncs email (not contacts or calendar), which means that we + // will hide the preference to turn syncing on and off. In this case, we want the + // sync frequency preference to also control whether or not syncing is enabled at + // all. If sync is turned off, we will display "sync never" regardless of what the + // numeric value we have stored says. + final android.accounts.Account androidAcct = new android.accounts.Account( + mAccount.mEmailAddress, mServiceInfo.accountType); + if ((Integer) newValue == Account.CHECK_INTERVAL_NEVER) { + // Disable syncing from the account manager. Leave the current sync frequency + // in the database. + ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY, + false); + } else { + // Enable syncing from the account manager. + ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY, + true); + cv.put(AccountColumns.SYNC_INTERVAL, Integer.parseInt(summary)); + } } - return false; + } else if (key.equals(PREFERENCE_SYNC_WINDOW)) { + final String summary = newValue.toString(); + int index = mSyncWindow.findIndexOfValue(summary); + mSyncWindow.setSummary(mSyncWindow.getEntries()[index]); + mSyncWindow.setValue(summary); + cv.put(AccountColumns.SYNC_LOOKBACK, Integer.parseInt(summary)); + } else if (key.equals(PREFERENCE_SYNC_EMAIL)) { + final android.accounts.Account androidAcct = new android.accounts.Account( + mAccount.mEmailAddress, mServiceInfo.accountType); + ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY, + (Boolean) newValue); + } else if (key.equals(PREFERENCE_SYNC_CONTACTS)) { + final android.accounts.Account androidAcct = new android.accounts.Account( + mAccount.mEmailAddress, mServiceInfo.accountType); + ContentResolver.setSyncAutomatically(androidAcct, ContactsContract.AUTHORITY, + (Boolean) newValue); + } else if (key.equals(PREFERENCE_SYNC_CALENDAR)) { + final android.accounts.Account androidAcct = new android.accounts.Account( + mAccount.mEmailAddress, mServiceInfo.accountType); + ContentResolver.setSyncAutomatically(androidAcct, CalendarContract.AUTHORITY, + (Boolean) newValue); + } else if (key.equals(PREFERENCE_BACKGROUND_ATTACHMENTS)) { + int newFlags = mAccount.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS); + + newFlags |= (Boolean) newValue ? + Account.FLAGS_BACKGROUND_ATTACHMENTS : 0; + + cv.put(AccountColumns.FLAGS, newFlags); + } else if (FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED.equals(key)) { + mInboxFolderPreferences.setNotificationsEnabled((Boolean) newValue); + return true; } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE.equals(key)) { final boolean vibrateSetting = (Boolean) newValue; mInboxVibrate.setChecked(vibrateSetting); mInboxFolderPreferences.setNotificationVibrateEnabled(vibrateSetting); - preferenceChanged(FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE, newValue); return true; - } else if (FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED.equals(key)) { - mInboxFolderPreferences.setNotificationsEnabled((Boolean) newValue); - preferenceChanged(FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED, newValue); + } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE.equals(key)) { return true; } else { // Default behavior, just indicate that the preferences were written - preferenceChanged(key, newValue); + LogUtils.d(LogUtils.TAG, "Unknown preference key %s", key); return true; } + if (cv.size() > 0) { + new UpdateTask().run(mContext.getContentResolver(), mAccount.getUri(), cv, null, null); + MailActivityEmail.setServicesEnabledAsync(mContext); + } + return false; } @Override @@ -351,14 +416,7 @@ public class AccountSettingsFragment extends PreferenceFragment } /** - * Activity provides callbacks here - */ - public void setCallback(Callback callback) { - mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; - } - - /** - * Async task to load account in order to view/edit it + * Async task loader to load account in order to view/edit it */ private static class AccountLoader extends MailAsyncTaskLoader> { public static final String RESULT_KEY_ACCOUNT = "account"; @@ -367,11 +425,13 @@ public class AccountSettingsFragment extends PreferenceFragment public static final String RESULT_KEY_INBOX = "inbox"; private final ForceLoadContentObserver mObserver; + private final String mAccountEmail; private final long mAccountId; - private AccountLoader(Context context, long accountId) { + private AccountLoader(Context context, String accountEmail, long accountId) { super(context); mObserver = new ForceLoadContentObserver(); + mAccountEmail = accountEmail; mAccountId = accountId; } @@ -379,7 +439,12 @@ public class AccountSettingsFragment extends PreferenceFragment public Map loadInBackground() { final Map map = new HashMap(); - Account account = Account.restoreAccountWithId(getContext(), mAccountId, mObserver); + final Account account; + if (!TextUtils.isEmpty(mAccountEmail)) { + account = Account.restoreAccountWithAddress(getContext(), mAccountEmail, mObserver); + } else { + account = Account.restoreAccountWithId(getContext(), mAccountId, mObserver); + } if (account == null) { return map; } @@ -399,7 +464,8 @@ public class AccountSettingsFragment extends PreferenceFragment Policy.restorePolicyWithId(getContext(), account.mPolicyKey, mObserver); final Cursor uiAccountCursor = getContext().getContentResolver().query( - EmailProvider.uiUri("uiaccount", mAccountId), UIProvider.ACCOUNTS_PROJECTION, + EmailProvider.uiUri("uiaccount", account.getId()), + UIProvider.ACCOUNTS_PROJECTION, null, null, null); if (uiAccountCursor != null) { @@ -457,6 +523,7 @@ public class AccountSettingsFragment extends PreferenceFragment private class AccountLoaderCallbacks implements LoaderManager.LoaderCallbacks> { + public static final String ARG_ACCOUNT_EMAIL = "accountEmail"; public static final String ARG_ACCOUNT_ID = "accountId"; private final Context mContext; @@ -466,9 +533,12 @@ public class AccountSettingsFragment extends PreferenceFragment @Override public void onLoadFinished(Loader> loader, Map data) { + final Activity activity = getActivity(); + if (activity == null) { + return; + } if (data == null) { - mSaveOnExit = false; - mCallback.abandonEdit(); + activity.finish(); return; } @@ -480,33 +550,33 @@ public class AccountSettingsFragment extends PreferenceFragment final Intent i = AccountSecurity.actionUpdateSecurityIntent(mContext, mAccount.getId(), true); mContext.startActivity(i); - mSaveOnExit = false; - mCallback.abandonEdit(); + activity.finish(); return; } final Folder inbox = (Folder) data.get(AccountLoader.RESULT_KEY_INBOX); if (mUiAccount == null || mAccount == null) { - mSaveOnExit = false; - mCallback.abandonEdit(); + activity.finish(); return; } + mServiceInfo = + EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext)); + if (inbox == null) { mInboxFolderPreferences = null; } else { mInboxFolderPreferences = new FolderPreferences(mContext, mUiAccount.getEmailAddress(), inbox, true); } - if (!mSaveOnExit) { - loadSettings(); - } + loadSettings(); } @Override public Loader> onCreateLoader(int id, Bundle args) { - return new AccountLoader(mContext, args.getLong(ARG_ACCOUNT_ID)); + return new AccountLoader(mContext, args.getString(ARG_ACCOUNT_EMAIL), + args.getLong(ARG_ACCOUNT_ID)); } @Override @@ -565,9 +635,6 @@ public class AccountSettingsFragment extends PreferenceFragment * Load account data into preference UI. This must be called on the main thread. */ private void loadSettings() { - // Once loaded the data is ready to be saved, as well - mSaveOnExit = false; - final AccountPreferences accountPreferences = new AccountPreferences(mContext, mUiAccount.getEmailAddress()); if (mInboxFolderPreferences != null) { @@ -576,9 +643,8 @@ public class AccountSettingsFragment extends PreferenceFragment } final String protocol = mAccount.getProtocol(mContext); - final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext, protocol); - if (info == null) { - LogUtils.e(Logging.LOG_TAG, + if (mServiceInfo == null) { + LogUtils.e(LogUtils.TAG, "Could not find service info for account %d with protocol %s", mAccount.mId, protocol); getActivity().onBackPressed(); @@ -608,9 +674,9 @@ public class AccountSettingsFragment extends PreferenceFragment R.string.preferences_signature_summary_not_set); mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); - mCheckFrequency.setEntries(info.syncIntervalStrings); - mCheckFrequency.setEntryValues(info.syncIntervals); - if (info.syncContacts || info.syncCalendar) { + mCheckFrequency.setEntries(mServiceInfo.syncIntervalStrings); + mCheckFrequency.setEntryValues(mServiceInfo.syncIntervals); + if (mServiceInfo.syncContacts || mServiceInfo.syncCalendar) { // This account allows syncing of contacts and/or calendar, so we will always have // separate preferences to enable or disable syncing of email, contacts, and calendar. // The "sync frequency" preference really just needs to control the frequency value @@ -637,7 +703,7 @@ public class AccountSettingsFragment extends PreferenceFragment new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - mCallback.onEditQuickResponses(mUiAccount); + onEditQuickResponses(mUiAccount); return true; } }); @@ -646,9 +712,10 @@ public class AccountSettingsFragment extends PreferenceFragment PreferenceCategory dataUsageCategory = (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE); - if (info.offerLookback) { + if (mServiceInfo.offerLookback) { if (mSyncWindow == null) { mSyncWindow = new ListPreference(mContext); + mSyncWindow.setKey(PREFERENCE_SYNC_WINDOW); dataUsageCategory.addPreference(mSyncWindow); } mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label); @@ -665,27 +732,17 @@ public class AccountSettingsFragment extends PreferenceFragment // Must correspond to the hole in the XML file that's reserved. mSyncWindow.setOrder(2); - mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - final String summary = newValue.toString(); - int index = mSyncWindow.findIndexOfValue(summary); - mSyncWindow.setSummary(mSyncWindow.getEntries()[index]); - mSyncWindow.setValue(summary); - preferenceChanged(preference.getKey(), newValue); - return false; - } - }); + mSyncWindow.setOnPreferenceChangeListener(this); } PreferenceCategory folderPrefs = (PreferenceCategory) findPreference(PREFERENCE_SYSTEM_FOLDERS); if (folderPrefs != null) { - if (info.requiresSetup) { + if (mServiceInfo.requiresSetup) { Preference trashPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_TRASH); Intent i = new Intent(mContext, FolderPickerActivity.class); Uri uri = EmailContent.CONTENT_URI.buildUpon().appendQueryParameter( - "account", Long.toString(mAccountId)).build(); + "account", Long.toString(mAccount.getId())).build(); i.setData(uri); i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_TRASH); trashPreference.setIntent(i); @@ -700,16 +757,15 @@ public class AccountSettingsFragment extends PreferenceFragment } } - mAccountBackgroundAttachments = (CheckBoxPreference) + CheckBoxPreference backgroundAttachments = (CheckBoxPreference) findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS); - if (mAccountBackgroundAttachments != null) { - if (!info.offerAttachmentPreload) { - dataUsageCategory.removePreference(mAccountBackgroundAttachments); - mAccountBackgroundAttachments = null; + if (backgroundAttachments != null) { + if (!mServiceInfo.offerAttachmentPreload) { + dataUsageCategory.removePreference(backgroundAttachments); } else { - mAccountBackgroundAttachments.setChecked( + backgroundAttachments.setChecked( 0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS)); - mAccountBackgroundAttachments.setOnPreferenceChangeListener(this); + backgroundAttachments.setOnPreferenceChangeListener(this); } } @@ -808,7 +864,7 @@ public class AccountSettingsFragment extends PreferenceFragment new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - mCallback.onIncomingSettings(mAccount); + onIncomingSettings(mAccount); return true; } }); @@ -816,21 +872,21 @@ public class AccountSettingsFragment extends PreferenceFragment // Hide the outgoing account setup link if it's not activated Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING); if (prefOutgoing != null) { - if (info.usesSmtp && mAccount.mHostAuthSend != null) { + if (mServiceInfo.usesSmtp && mAccount.mHostAuthSend != null) { prefOutgoing.setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - mCallback.onOutgoingSettings(mAccount); + onOutgoingSettings(mAccount); return true; } }); } else { - if (info.usesSmtp) { + if (mServiceInfo.usesSmtp) { // We really ought to have an outgoing host auth but we don't. // There's nothing we can do at this point, so just log the error. - LogUtils.e(Logging.LOG_TAG, "Account %d has a bad outbound hostauth", - mAccountId); + LogUtils.e(LogUtils.TAG, "Account %d has a bad outbound hostauth", + mAccount.getId()); } PreferenceCategory serverCategory = (PreferenceCategory) findPreference( PREFERENCE_CATEGORY_SERVER); @@ -838,121 +894,41 @@ public class AccountSettingsFragment extends PreferenceFragment } } - mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS); - mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR); - mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL); - if (mSyncContacts != null && mSyncCalendar != null && mSyncEmail != null) { - if (info.syncContacts || info.syncCalendar) { - if (info.syncContacts) { - mSyncContacts.setChecked(ContentResolver + final CheckBoxPreference syncContacts = + (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS); + final CheckBoxPreference syncCalendar = + (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR); + final CheckBoxPreference syncEmail = + (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL); + if (syncContacts != null && syncCalendar != null && syncEmail != null) { + if (mServiceInfo.syncContacts || mServiceInfo.syncCalendar) { + if (mServiceInfo.syncContacts) { + syncContacts.setChecked(ContentResolver .getSyncAutomatically(androidAcct, ContactsContract.AUTHORITY)); - mSyncContacts.setOnPreferenceChangeListener(this); + syncContacts.setOnPreferenceChangeListener(this); } else { - mSyncContacts.setChecked(false); - mSyncContacts.setEnabled(false); + syncContacts.setChecked(false); + syncContacts.setEnabled(false); } - if (info.syncCalendar) { - mSyncCalendar.setChecked(ContentResolver + if (mServiceInfo.syncCalendar) { + syncCalendar.setChecked(ContentResolver .getSyncAutomatically(androidAcct, CalendarContract.AUTHORITY)); - mSyncCalendar.setOnPreferenceChangeListener(this); + syncCalendar.setOnPreferenceChangeListener(this); } else { - mSyncCalendar.setChecked(false); - mSyncCalendar.setEnabled(false); + syncCalendar.setChecked(false); + syncCalendar.setEnabled(false); } - mSyncEmail.setChecked(ContentResolver + syncEmail.setChecked(ContentResolver .getSyncAutomatically(androidAcct, EmailContent.AUTHORITY)); - mSyncEmail.setOnPreferenceChangeListener(this); + syncEmail.setOnPreferenceChangeListener(this); } else { - dataUsageCategory.removePreference(mSyncContacts); - mSyncContacts = null; - dataUsageCategory.removePreference(mSyncCalendar); - mSyncCalendar = null; - dataUsageCategory.removePreference(mSyncEmail); - mSyncEmail = null; + dataUsageCategory.removePreference(syncContacts); + dataUsageCategory.removePreference(syncCalendar); + dataUsageCategory.removePreference(syncEmail); } } } - /** - * Called any time a preference is changed. - */ - private void preferenceChanged(String preference, Object value) { - mCallback.onSettingsChanged(mAccountId, preference, value); - mSaveOnExit = true; - } - - /* - * Note: This writes the settings on the UI thread. This has to be done so the settings are - * committed before we might be killed. - */ - private void saveSettings() { - // Turn off all controlled flags - will turn them back on while checking UI elements - int newFlags = mAccount.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS); - - if (mAccountBackgroundAttachments != null) { - newFlags |= mAccountBackgroundAttachments.isChecked() ? - Account.FLAGS_BACKGROUND_ATTACHMENTS : 0; - } - - final EmailServiceInfo info = - EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext)); - final android.accounts.Account androidAcct = new android.accounts.Account( - mAccount.mEmailAddress, info.accountType); - - // If the display name has been cleared, we'll reset it to the default value (email addr) - mAccount.setDisplayName(mAccountDescription.getText().trim()); - // The sender name must never be empty (this is enforced by the preference editor) - mAccount.setSenderName(mAccountName.getText().trim()); - mAccount.setSignature(mAccountSignature.getText()); - int freq = Integer.parseInt(mCheckFrequency.getValue()); - if (info.syncContacts || info.syncCalendar) { - // This account allows syncing of contacts and/or calendar, so we will always have - // separate preferences to enable or disable syncing of email, contacts, and calendar. - // The "sync frequency" preference really just needs to control the frequency value - // in our database. - mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue())); - } else { - // This account only syncs email (not contacts or calendar), which means that we will - // hide the preference to turn syncing on and off. In this case, we want the sync - // frequency preference to also control whether or not syncing is enabled at all. If - // sync is turned off, we will display "sync never" regardless of what the numeric - // value we have stored says. - if (freq == Account.CHECK_INTERVAL_NEVER) { - // Disable syncing from the account manager. Leave the current sync frequency - // in the database. - ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY, false); - } else { - // Enable syncing from the account manager. - ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY, true); - mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue())); - } - } - if (mSyncWindow != null) { - mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue())); - } - mAccount.setFlags(newFlags); - - if (info.syncContacts || info.syncCalendar) { - ContentResolver.setSyncAutomatically(androidAcct, ContactsContract.AUTHORITY, - mSyncContacts.isChecked()); - ContentResolver.setSyncAutomatically(androidAcct, CalendarContract.AUTHORITY, - mSyncCalendar.isChecked()); - ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY, - mSyncEmail.isChecked()); - } - - // Commit the changes - // Note, this is done in the UI thread because at this point, we must commit - // all changes - any time after onPause completes, we could be killed. This is analogous - // to the way that SharedPreferences tries to work off-thread in apply(), but will pause - // until completion in onPause(). - ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount); - mAccount.update(mContext, cv); - - // Run the remaining changes off-thread - MailActivityEmail.setServicesEnabledAsync(mContext); - } - /** * Shows the system ringtone picker. */ @@ -969,4 +945,100 @@ public class AccountSettingsFragment extends PreferenceFragment intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); startActivityForResult(intent, RINGTONE_REQUEST_CODE); } + + /** + * Dispatch to edit quick responses. + */ + public void onEditQuickResponses(com.android.mail.providers.Account account) { + final Bundle args = AccountSettingsEditQuickResponsesFragment.createArgs(account); + final PreferenceActivity activity = (PreferenceActivity) getActivity(); + activity.startPreferencePanel(AccountSettingsEditQuickResponsesFragment.class.getName(), + args, R.string.account_settings_edit_quick_responses_label, null, null, 0); + } + + /** + * Dispatch to edit incoming settings. + */ + public void onIncomingSettings(Account account) { + final Intent intent = + AccountServerSettingsActivity.getIntentForIncoming(getActivity(), account); + getActivity().startActivity(intent); + } + + /** + * Dispatch to edit outgoing settings. + */ + public void onOutgoingSettings(Account account) { + final Intent intent = + AccountServerSettingsActivity.getIntentForOutgoing(getActivity(), account); + getActivity().startActivity(intent); + } + + /** + * Dialog briefly shown in some cases, to indicate the user that login failed. If the user + * clicks OK, we simply dismiss the dialog, leaving the user in the account settings for + * that account; If the user clicks "cancel", we exit account settings. + */ + public static class LoginWarningDialog extends DialogFragment + implements DialogInterface.OnClickListener { + private static final String BUNDLE_KEY_ACCOUNT_NAME = "account_name"; + private String mReason; + + // Public no-args constructor needed for fragment re-instantiation + public LoginWarningDialog() {} + + /** + * Create a new dialog. + */ + public static LoginWarningDialog newInstance(String accountName, String reason) { + final LoginWarningDialog dialog = new LoginWarningDialog(); + final Bundle b = new Bundle(1); + b.putString(BUNDLE_KEY_ACCOUNT_NAME, accountName); + dialog.setArguments(b); + dialog.mReason = reason; + return dialog; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final String accountName = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME); + + final Context context = getActivity(); + final Resources res = context.getResources(); + final AlertDialog.Builder b = new AlertDialog.Builder(context); + b.setTitle(R.string.account_settings_login_dialog_title); + b.setIconAttribute(android.R.attr.alertDialogIcon); + if (mReason != null) { + final TextView message = new TextView(context); + final String alert = res.getString( + R.string.account_settings_login_dialog_reason_fmt, accountName, mReason); + SpannableString spannableAlertString = new SpannableString(alert); + Linkify.addLinks(spannableAlertString, Linkify.WEB_URLS); + message.setText(spannableAlertString); + // There must be a better way than specifying size/padding this way + // It does work and look right, though + final int textSize = res.getDimensionPixelSize(R.dimen.dialog_text_size); + message.setTextSize(textSize); + final int paddingLeft = res.getDimensionPixelSize(R.dimen.dialog_padding_left); + final int paddingOther = res.getDimensionPixelSize(R.dimen.dialog_padding_other); + message.setPadding(paddingLeft, paddingOther, paddingOther, paddingOther); + message.setMovementMethod(LinkMovementMethod.getInstance()); + b.setView(message); + } else { + b.setMessage(res.getString(R.string.account_settings_login_dialog_content_fmt, + accountName)); + } + b.setPositiveButton(android.R.string.ok, this); + b.setNegativeButton(android.R.string.cancel, this); + return b.create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + if (which == DialogInterface.BUTTON_NEGATIVE) { + getActivity().finish(); + } + } + } } diff --git a/src/com/android/email/activity/setup/HeadlessAccountSettingsLoader.java b/src/com/android/email/activity/setup/HeadlessAccountSettingsLoader.java index ba43f9d7a..8c51fd847 100644 --- a/src/com/android/email/activity/setup/HeadlessAccountSettingsLoader.java +++ b/src/com/android/email/activity/setup/HeadlessAccountSettingsLoader.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.preference.PreferenceActivity; import com.android.emailcommon.provider.Account; import com.android.emailcommon.utility.IntentUtilities; @@ -46,22 +45,10 @@ public class HeadlessAccountSettingsLoader extends Activity { protected void onPostExecute(Account result) { // create an Intent to view a new activity - final Intent intent = new Intent(Intent.ACTION_VIEW); + final Intent intent = + AccountServerSettingsActivity.getIntentForIncoming(mContext, result); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // we are navigating explicitly to the AccountSettings activity - intent.setClass(mContext, AccountSettings.class); - - // place the account in the intent as an extra - intent.putExtra(AccountSettings.EXTRA_ACCOUNT, result); - - // these extras show the "incoming fragment" in the AccountSettings activity by default - intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, - AccountSetupIncomingFragment.class.getCanonicalName()); - intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, - AccountSetupIncomingFragment.getArgs(true)); - intent.putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true); - mContext.startActivity(intent); finish(); diff --git a/src/com/android/email/activity/setup/SetupDataFragment.java b/src/com/android/email/activity/setup/SetupDataFragment.java index df9714cdb..45b7cf1ad 100644 --- a/src/com/android/email/activity/setup/SetupDataFragment.java +++ b/src/com/android/email/activity/setup/SetupDataFragment.java @@ -76,16 +76,6 @@ public class SetupDataFragment extends Fragment implements Parcelable { mCredentialResults = null; } - public SetupDataFragment(int flowMode) { - this(); - mFlowMode = flowMode; - } - - public SetupDataFragment(int flowMode, Account account) { - this(flowMode); - setAccount(account); - } - @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/tests/src/com/android/email/activity/setup/AccountSettingsTests.java b/tests/src/com/android/email/activity/setup/AccountSettingsTests.java index 91115237f..eb6265421 100644 --- a/tests/src/com/android/email/activity/setup/AccountSettingsTests.java +++ b/tests/src/com/android/email/activity/setup/AccountSettingsTests.java @@ -128,9 +128,9 @@ public class AccountSettingsTests extends ActivityInstrumentationTestCase2