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
This commit is contained in:
Tony Mantler 2014-06-25 13:23:21 -07:00
parent ff52eef8e6
commit 06415a635f
13 changed files with 677 additions and 1010 deletions

View File

@ -272,30 +272,40 @@
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- Must be exported in order for the AccountManager to launch it -->
<activity
android:name=".activity.setup.AccountSettings"
android:label="@string/settings_activity_title"
android:exported="true"
>
android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email.android.com"
android:pathPrefix="/settings"
/>
</intent-filter>
</activity>
<activity
android:name="com.android.mail.ui.settings.PublicPreferenceActivity"
android:label="@string/settings_activity_title"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay" >
<!-- Must be exported in order for the AccountManager to launch it -->
<intent-filter>
<action android:name="com.android.email.activity.setup.ACCOUNT_MANAGER_ENTRY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email.android.com"
android:pathPrefix="/settings"
/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".activity.setup.AccountServerSettingsActivity"
android:label="@string/mailbox_settings_activity_title"
android:exported="false">
</activity>
<!-- a Headless Activity to load the account from the account id before navigating to the
Incoming Account Settings fragment -->
<activity

View File

@ -215,6 +215,23 @@ public final class Account extends EmailContent implements Parcelable {
Account.CONTENT_URI, Account.CONTENT_PROJECTION, id, observer);
}
public static Account restoreAccountWithAddress(Context context, String emailAddress) {
return restoreAccountWithAddress(context, emailAddress, null);
}
public static Account restoreAccountWithAddress(Context context, String emailAddress,
ContentObserver observer) {
final Cursor c = context.getContentResolver().query(CONTENT_URI,
new String[] {AccountColumns._ID},
AccountColumns.EMAIL_ADDRESS + "=?", new String[] {emailAddress},
null);
if (c == null || !c.moveToFirst()) {
return null;
}
final long id = c.getLong(c.getColumnIndex(AccountColumns._ID));
return restoreAccountWithId(context, id, observer);
}
@Override
protected Uri getContentNotificationUri() {
return Account.CONTENT_URI;
@ -323,6 +340,7 @@ public final class Account extends EmailContent implements Parcelable {
return mSignature;
}
@VisibleForTesting
public void setSignature(String signature) {
mSignature = signature;
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/account_server_settings_container">
</ScrollView>

View File

@ -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();
}
}
}

View File

@ -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<Header> 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<Header> 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<Header> 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<Long, Void, Object[]> {
@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<Intent, Void, Long> {
@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;
}
}

View File

@ -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);
}

View File

@ -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<Map<String, Object>> {
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<String, Object> loadInBackground() {
final Map<String, Object> map = new HashMap<String, Object>();
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<Map<String, Object>> {
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<Map<String, Object>> loader, Map<String, Object> 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<Map<String, Object>> 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();
}
}
}
}

View File

@ -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();

View File

@ -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);

View File

@ -128,9 +128,9 @@ public class AccountSettingsTests extends ActivityInstrumentationTestCase2<Accou
runTestOnUiThread(new Runnable() {
public void run() {
AccountSettingsFragment f = theActivity.getSettingsFragment();
mCheckFrequency =
(ListPreference) f.findPreference(PREFERENCE_FREQUENCY);
//AccountSettingsFragment f = theActivity.getSettingsFragment();
//mCheckFrequency =
// (ListPreference) f.findPreference(PREFERENCE_FREQUENCY);
}
});
}

View File

@ -197,7 +197,9 @@ public class AccountSetupIncomingTests extends
final HostAuth auth = account.getOrCreateHostAuthRecv(context);
auth.setHostAuthFromString(storeUriString);
final SetupDataFragment setupDataFragment =
new SetupDataFragment(SetupDataFragment.FLOW_MODE_NORMAL, account);
new SetupDataFragment();
setupDataFragment.setFlowMode(SetupDataFragment.FLOW_MODE_NORMAL);
setupDataFragment.setAccount(account);
final Intent i = new Intent(AccountSetupFinal.ACTION_JUMP_TO_INCOMING);
i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupDataFragment);
return i;

View File

@ -172,7 +172,9 @@ public class AccountSetupOptionsTests
final HostAuth auth = account.getOrCreateHostAuthRecv(context);
auth.setHostAuthFromString(storeUri);
final SetupDataFragment setupDataFragment =
new SetupDataFragment(SetupDataFragment.FLOW_MODE_NORMAL, account);
new SetupDataFragment();
setupDataFragment.setFlowMode(SetupDataFragment.FLOW_MODE_NORMAL);
setupDataFragment.setAccount(account);
final Intent i = new Intent(AccountSetupFinal.ACTION_JUMP_TO_OPTIONS);
i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupDataFragment);
return i;

View File

@ -202,7 +202,9 @@ public class AccountSetupOutgoingTests extends
final HostAuth auth = account.getOrCreateHostAuthSend(context);
auth.setHostAuthFromString(senderUriString);
final SetupDataFragment setupDataFragment =
new SetupDataFragment(SetupDataFragment.FLOW_MODE_NORMAL, account);
new SetupDataFragment();
setupDataFragment.setFlowMode(SetupDataFragment.FLOW_MODE_NORMAL);
setupDataFragment.setAccount(account);
final Intent i = new Intent(AccountSetupFinal.ACTION_JUMP_TO_OUTGOING);
i.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, setupDataFragment);
return i;