/* * Copyright (C) 2010 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.accounts.AccountManager; import android.app.Activity; import android.app.AlertDialog; import android.app.LoaderManager; import android.app.ProgressDialog; 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; import android.media.RingtoneManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Handler.Callback; import android.os.Message; import android.os.Vibrator; 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.PreferenceScreen; import android.provider.CalendarContract; import android.provider.ContactsContract; import android.provider.Settings; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.Window; import android.widget.Toast; import com.android.email.R; import com.android.email.SecurityPolicy; import com.android.email.provider.EmailProvider; import com.android.email.provider.FolderPickerActivity; import com.android.email.service.EmailServiceUtils; import com.android.email.service.EmailServiceUtils.EmailServiceInfo; 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.emailcommon.service.EmailServiceProxy; import com.android.mail.preferences.AccountPreferences; import com.android.mail.preferences.FolderPreferences; import com.android.mail.preferences.FolderPreferences.NotificationLight; import com.android.mail.preferences.notifications.FolderNotificationLightPreference; 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.PublicPreferenceActivity; 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; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Fragment containing the main logic for account settings. This also calls out to other * fragments for server settings. * * TODO: Can we defer calling addPreferencesFromResource() until after we load the account? This * could reduce flicker. */ public class AccountSettingsFragment extends MailAccountPrefsFragment implements Preference.OnPreferenceChangeListener { private static final String ARG_ACCOUNT_ID = "account_id"; 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_SETTINGS = MailboxSettings.PREFERENCE_SYNC_SETTINGS; 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_PER_FOLDER_NOTIFICATIONS = MailboxSettings.PREFERENCE_PER_FOLDER_NOTIFICATIONS; private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage"; private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications"; private static final String PREFERENCE_CATEGORY_SERVER = "account_servers"; private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies"; @SuppressWarnings("unused") // temporarily unused pending policy UI private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced"; @SuppressWarnings("unused") // temporarily unused pending policy UI private static final String PREFERENCE_POLICIES_UNSUPPORTED = "policies_unsupported"; 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_SYSTEM_FOLDERS = "system_folders"; private static final String PREFERENCE_SYSTEM_FOLDERS_TRASH = "system_folders_trash"; private static final String PREFERENCE_SYSTEM_FOLDERS_SENT = "system_folders_sent"; private static final String SAVESTATE_SYNC_INTERVALS = "savestate_sync_intervals"; private static final String SAVESTATE_SYNC_INTERVAL_STRINGS = "savestate_sync_interval_strings"; // Request code to start different activities. private static final int RINGTONE_REQUEST_CODE = 0; // Message codes private static final int MSG_DELETE_ACCOUNT = 0; private EditTextPreference mAccountDescription; private EditTextPreference mAccountName; private EditTextPreference mAccountSignature; private ListPreference mCheckFrequency; private ListPreference mSyncWindow; private Preference mSyncSettings; private CheckBoxPreference mInboxVibrate; private Preference mInboxRingtone; private FolderNotificationLightPreference mInboxLights; private Preference mPerFolderNotification; private Context mContext; private Handler mHandler; private Account mAccount; private com.android.mail.providers.Account mUiAccount; private EmailServiceInfo mServiceInfo; private Folder mInboxFolder; private Ringtone mRingtone; private MenuItem mDeleteAccountItem; private final Callback mHandlerCallback = new Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == MSG_DELETE_ACCOUNT) { deleteAccount(); } return false; } }; /** * This may be null if the account exists but the inbox has not yet been created in the database * (waiting for initial sync) */ private FolderPreferences mInboxFolderPreferences; // The email of the account being edited private String mAccountEmail; /** * If launching with an email address, use this method to build the arguments. */ public static Bundle buildArguments(final String email) { final Bundle b = new Bundle(1); b.putString(ARG_ACCOUNT_EMAIL, email); return b; } /** * If launching with an account ID, use this method to build the arguments. */ public static Bundle buildArguments(final long accountId) { final Bundle b = new Bundle(1); b.putLong(ARG_ACCOUNT_ID, accountId); return b; } @Override public void onAttach(Activity activity) { super.onAttach(activity); mContext = activity; } /** * Called to do initial creation of a fragment. This is called after * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler = new Handler(mHandlerCallback); setHasOptionsMenu(true); // Load the preferences from an XML resource addPreferencesFromResource(R.xml.account_settings_preferences); if (!getResources().getBoolean(R.bool.quickresponse_supported)) { final Preference quickResponsePref = findPreference(PREFERENCE_QUICK_RESPONSES); if (quickResponsePref != null) { getPreferenceScreen().removePreference(quickResponsePref); } } // Start loading the account data, if provided in the arguments // If not, activity must call startLoadingAccount() directly Bundle b = getArguments(); if (b != null) { 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 // our loader completes. The problem is, that if the sync frequency chooser is // displayed when the screen rotates, it reinitializes it to the defaults, and doesn't // correct it after the loader finishes again. See b/13624066 // To work around this, we'll save the current set of sync interval values and strings, // in onSavedInstanceState, and restore them here. final CharSequence [] syncIntervalStrings = savedInstanceState.getCharSequenceArray(SAVESTATE_SYNC_INTERVAL_STRINGS); final CharSequence [] syncIntervals = savedInstanceState.getCharSequenceArray(SAVESTATE_SYNC_INTERVALS); mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); fillCheckFrecuency(syncIntervalStrings, syncIntervals); } } @Override public void onSaveInstanceState(@NonNull Bundle outstate) { super.onSaveInstanceState(outstate); if (mCheckFrequency != null) { outstate.putCharSequenceArray(SAVESTATE_SYNC_INTERVAL_STRINGS, mCheckFrequency.getEntries()); outstate.putCharSequenceArray(SAVESTATE_SYNC_INTERVALS, mCheckFrequency.getEntryValues()); } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final Bundle args = new Bundle(1); 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 onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case RINGTONE_REQUEST_CODE: if (resultCode == Activity.RESULT_OK && data != null) { Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); setRingtone(uri); } break; } } /** * Sets the current ringtone. */ private void setRingtone(Uri ringtone) { if (ringtone != null) { mInboxFolderPreferences.setNotificationRingtoneUri(ringtone.toString()); mRingtone = RingtoneManager.getRingtone(getActivity(), ringtone); } else { // Null means silent was selected. mInboxFolderPreferences.setNotificationRingtoneUri(""); mRingtone = null; } setRingtoneSummary(); } private void setRingtoneSummary() { final String summary = mRingtone != null ? mRingtone.getTitle(mContext) : mContext.getString(R.string.silent_ringtone); mInboxRingtone.setSummary(summary); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, @NonNull Preference preference) { final String key = preference.getKey(); if (key.equals(PREFERENCE_SYNC_SETTINGS) || key.equals(PREFERENCE_PER_FOLDER_NOTIFICATIONS)) { startActivity(MailboxSettings.getIntent(getActivity(), mUiAccount.fullFolderListUri, mInboxFolder, key)); return true; } else { return super.onPreferenceTreeClick(preferenceScreen, preference); } } /** * Listen to all preference changes in this class. * @param preference The changed Preference * @param newValue The new value of the Preference * @return True to update the state of the Preference with the new value */ @Override 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)) { summary = mUiAccount.getEmailAddress(); } mAccountDescription.setSummary(summary); mAccountDescription.setText(summary); 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 // maximum flexibility, e.g. the ability to indent String signature = newValue.toString(); if (signature.trim().isEmpty()) { signature = ""; } mAccountSignature.setText(signature); SettingsUtils.updatePreferenceSummary(mAccountSignature, signature, R.string.preferences_signature_summary_not_set); 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.parseInt(summary) == Account.CHECK_INTERVAL_NEVER) { // Disable syncing from the account manager. 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)); } } 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); loadSettings(); } 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); loadSettings(); } 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); loadSettings(); } 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); return true; } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_LIGHTS.equals(key)) { final String LightsSettings = (String) newValue; NotificationLight notificationLight = NotificationLight.fromStringPref(LightsSettings); updateNotificationLight(notificationLight); mInboxFolderPreferences.setNotificationLights(notificationLight); return true; } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE.equals(key)) { return true; } else { // Default behavior, just indicate that the preferences were written 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); EmailProvider.setServicesEnabledAsync(mContext); } return false; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); // Delete account item mDeleteAccountItem = menu.add(Menu.NONE, Menu.NONE, 0, R.string.delete_account); mDeleteAccountItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); inflater.inflate(R.menu.settings_fragment_menu, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.equals(mDeleteAccountItem)) { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(mContext); alertBuilder.setTitle(R.string.delete_account); String msg = getString(R.string.delete_account_confirmation_msg, mAccountEmail); alertBuilder.setMessage(msg); alertBuilder.setCancelable(true); final DialogInterface.OnClickListener cb = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); mHandler.dispatchMessage(Message.obtain(mHandler, MSG_DELETE_ACCOUNT)); } }; alertBuilder.setPositiveButton(android.R.string.ok, cb); alertBuilder.setNegativeButton(android.R.string.cancel, null); alertBuilder.create().show(); return true; } return super.onOptionsItemSelected(item); } /** * 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"; private static final String RESULT_KEY_UIACCOUNT_CURSOR = "uiAccountCursor"; public static final String RESULT_KEY_UIACCOUNT = "uiAccount"; public static final String RESULT_KEY_INBOX = "inbox"; private final ForceLoadContentObserver mObserver; private final String mAccountEmail; private final long mAccountId; private AccountLoader(Context context, String accountEmail, long accountId) { super(context); mObserver = new ForceLoadContentObserver(); mAccountEmail = accountEmail; mAccountId = accountId; } @Override public Map loadInBackground() { final Map map = new HashMap<>(); 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; } map.put(RESULT_KEY_ACCOUNT, account); // We don't monitor these for changes, but they probably won't change in any meaningful // way account.getOrCreateHostAuthRecv(getContext()); account.getOrCreateHostAuthSend(getContext()); if (account.mHostAuthRecv == null) { return map; } account.mPolicy = Policy.restorePolicyWithId(getContext(), account.mPolicyKey, mObserver); final Cursor uiAccountCursor = getContext().getContentResolver().query( EmailProvider.uiUri("uiaccount", account.getId()), UIProvider.ACCOUNTS_PROJECTION, null, null, null); if (uiAccountCursor != null) { map.put(RESULT_KEY_UIACCOUNT_CURSOR, uiAccountCursor); uiAccountCursor.registerContentObserver(mObserver); } else { return map; } if (!uiAccountCursor.moveToFirst()) { return map; } final com.android.mail.providers.Account uiAccount = com.android.mail.providers.Account.builder().buildFrom(uiAccountCursor); map.put(RESULT_KEY_UIACCOUNT, uiAccount); final Cursor folderCursor = getContext().getContentResolver().query( uiAccount.settings.defaultInbox, UIProvider.FOLDERS_PROJECTION, null, null, null); final Folder inbox; try { if (folderCursor != null && folderCursor.moveToFirst()) { inbox = new Folder(folderCursor); } else { return map; } } finally { if (folderCursor != null) { folderCursor.close(); } } map.put(RESULT_KEY_INBOX, inbox); return map; } @Override protected void onDiscardResult(Map result) { final Account account = (Account) result.get(RESULT_KEY_ACCOUNT); if (account != null) { if (account.mPolicy != null) { account.mPolicy.close(getContext()); } account.close(getContext()); } final Cursor uiAccountCursor = (Cursor) result.get(RESULT_KEY_UIACCOUNT_CURSOR); if (uiAccountCursor != null) { uiAccountCursor.close(); } } } 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; private AccountLoaderCallbacks(Context context) { mContext = context; } @Override public void onLoadFinished(Loader> loader, Map data) { final Activity activity = getActivity(); if (activity == null) { return; } if (data == null) { activity.finish(); return; } mUiAccount = (com.android.mail.providers.Account) data.get(AccountLoader.RESULT_KEY_UIACCOUNT); mAccount = (Account) data.get(AccountLoader.RESULT_KEY_ACCOUNT); if (mAccount != null && (mAccount.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) { final Intent i = AccountSecurity.actionUpdateSecurityIntent(mContext, mAccount.getId(), true); mContext.startActivity(i); activity.finish(); return; } mInboxFolder = (Folder) data.get(AccountLoader.RESULT_KEY_INBOX); if (mUiAccount == null || mAccount == null) { activity.finish(); return; } mServiceInfo = EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext)); if (mInboxFolder == null) { mInboxFolderPreferences = null; } else { mInboxFolderPreferences = new FolderPreferences(mContext, mUiAccount.getEmailAddress(), mInboxFolder, true); } loadSettings(); } @Override public Loader> onCreateLoader(int id, Bundle args) { return new AccountLoader(mContext, args.getString(ARG_ACCOUNT_EMAIL), args.getLong(ARG_ACCOUNT_ID)); } @Override public void onLoaderReset(Loader> loader) {} } /** * From a Policy, create and return an ArrayList of Strings that describe (simply) those * policies that are supported by the OS. At the moment, the strings are simple (e.g. * "password required"); we should probably add more information (# characters, etc.), though */ @SuppressWarnings("unused") // temporarily unused pending policy UI private ArrayList getSystemPoliciesList(Policy policy) { Resources res = mContext.getResources(); ArrayList policies = new ArrayList<>(); if (policy.mPasswordMode != Policy.PASSWORD_MODE_NONE) { policies.add(res.getString(R.string.policy_require_password)); } if (policy.mPasswordHistory > 0) { policies.add(res.getString(R.string.policy_password_history)); } if (policy.mPasswordExpirationDays > 0) { policies.add(res.getString(R.string.policy_password_expiration)); } if (policy.mMaxScreenLockTime > 0) { policies.add(res.getString(R.string.policy_screen_timeout)); } if (policy.mDontAllowCamera) { policies.add(res.getString(R.string.policy_dont_allow_camera)); } if (policy.mMaxEmailLookback != 0) { policies.add(res.getString(R.string.policy_email_age)); } if (policy.mMaxCalendarLookback != 0) { policies.add(res.getString(R.string.policy_calendar_age)); } return policies; } @SuppressWarnings("unused") // temporarily unused pending policy UI private void setPolicyListSummary(ArrayList policies, String policiesToAdd, String preferenceName) { Policy.addPolicyStringToList(policiesToAdd, policies); if (policies.size() > 0) { Preference p = findPreference(preferenceName); StringBuilder sb = new StringBuilder(); for (String desc: policies) { sb.append(desc); sb.append('\n'); } p.setSummary(sb.toString()); } } /** * Load account data into preference UI. This must be called on the main thread. */ private void loadSettings() { final AccountPreferences accountPreferences = new AccountPreferences(mContext, mUiAccount.getEmailAddress()); if (mInboxFolderPreferences != null) { NotificationUtils.moveNotificationSetting( accountPreferences, mInboxFolderPreferences); } final String protocol = mAccount.getProtocol(mContext); if (mServiceInfo == null) { LogUtils.e(LogUtils.TAG, "Could not find service info for account %d with protocol %s", mAccount.mId, protocol); getActivity().onBackPressed(); // TODO: put up some sort of dialog/toast here to tell the user something went wrong return; } final android.accounts.Account androidAcct = mUiAccount.getAccountManagerAccount(); mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION); mAccountDescription.setSummary(mAccount.getDisplayName()); mAccountDescription.setText(mAccount.getDisplayName()); mAccountDescription.setOnPreferenceChangeListener(this); mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME); String senderName = mUiAccount.getSenderName(); // In rare cases, sendername will be null; Change this to empty string to avoid NPE's if (senderName == null) { senderName = ""; } mAccountName.setSummary(senderName); mAccountName.setText(senderName); mAccountName.setOnPreferenceChangeListener(this); final String accountSignature = mAccount.getSignature(); mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE); mAccountSignature.setText(accountSignature); mAccountSignature.setOnPreferenceChangeListener(this); SettingsUtils.updatePreferenceSummary(mAccountSignature, accountSignature, R.string.preferences_signature_summary_not_set); mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); fillCheckFrecuency(mServiceInfo.syncIntervalStrings, 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 // in our database. mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval())); } 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. boolean synced = ContentResolver.getSyncAutomatically(androidAcct, EmailContent.AUTHORITY); if (synced) { mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval())); } else { mCheckFrequency.setValue(String.valueOf(Account.CHECK_INTERVAL_NEVER)); } } mCheckFrequency.setSummary(mCheckFrequency.getEntry()); mCheckFrequency.setOnPreferenceChangeListener(this); final Preference quickResponsePref = findPreference(PREFERENCE_QUICK_RESPONSES); if (quickResponsePref != null) { quickResponsePref.setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { onEditQuickResponses(mUiAccount); return true; } }); } // Add check window preference final PreferenceCategory dataUsageCategory = (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE); 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); mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback())); final int maxLookback; if (mAccount.mPolicy != null) { maxLookback = mAccount.mPolicy.mMaxEmailLookback; } else { maxLookback = 0; } MailboxSettings.setupLookbackPreferenceOptions(mContext, mSyncWindow, maxLookback, false); // Must correspond to the hole in the XML file that's reserved. mSyncWindow.setOrder(2); mSyncWindow.setOnPreferenceChangeListener(this); if (mSyncSettings == null) { mSyncSettings = new Preference(mContext); mSyncSettings.setKey(PREFERENCE_SYNC_SETTINGS); dataUsageCategory.addPreference(mSyncSettings); } mSyncSettings.setTitle(R.string.folder_sync_settings_pref_title); mSyncSettings.setOrder(3); } final PreferenceCategory folderPrefs = (PreferenceCategory) findPreference(PREFERENCE_SYSTEM_FOLDERS); if (folderPrefs != null) { 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(mAccount.getId())).build(); i.setData(uri); i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_TRASH); trashPreference.setIntent(i); Preference sentPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_SENT); i = new Intent(mContext, FolderPickerActivity.class); i.setData(uri); i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_SENT); sentPreference.setIntent(i); } else { getPreferenceScreen().removePreference(folderPrefs); } } final CheckBoxPreference backgroundAttachments = (CheckBoxPreference) findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS); if (backgroundAttachments != null) { if (!mServiceInfo.offerAttachmentPreload) { dataUsageCategory.removePreference(backgroundAttachments); } else { backgroundAttachments.setChecked( 0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS)); backgroundAttachments.setOnPreferenceChangeListener(this); } } final PreferenceCategory notificationsCategory = (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS); if (mInboxFolderPreferences != null) { if (mServiceInfo.offerLookback) { // This account supports per-folder notifications // Enable per-folder notifications preference if (mPerFolderNotification == null) { mPerFolderNotification = new Preference(mContext); mPerFolderNotification.setKey(PREFERENCE_PER_FOLDER_NOTIFICATIONS); notificationsCategory.addPreference(mPerFolderNotification); } mPerFolderNotification.setTitle(R.string.folder_notify_settings_pref_title); // Remove Inbox per-account preferences final CheckBoxPreference inboxNotify = (CheckBoxPreference) findPreference( FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED); if (inboxNotify != null) { notificationsCategory.removePreference(inboxNotify); } mInboxRingtone = findPreference( FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE); if (mInboxRingtone != null) { notificationsCategory.removePreference(mInboxRingtone); } mInboxVibrate = (CheckBoxPreference) findPreference( FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE); if (mInboxVibrate != null) { notificationsCategory.removePreference(mInboxVibrate); } mInboxLights = (FolderNotificationLightPreference) findPreference( FolderPreferences.PreferenceKeys.NOTIFICATION_LIGHTS); if (mInboxLights != null) { notificationsCategory.removePreference(mInboxLights); } notificationsCategory.setEnabled(true); } else { final CheckBoxPreference inboxNotify = (CheckBoxPreference) findPreference( FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED); inboxNotify.setChecked(mInboxFolderPreferences.areNotificationsEnabled()); inboxNotify.setOnPreferenceChangeListener(this); mInboxRingtone = findPreference( FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE); final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri(); if (!TextUtils.isEmpty(ringtoneUri)) { mRingtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(ringtoneUri)); } setRingtoneSummary(); mInboxRingtone.setOnPreferenceChangeListener(this); mInboxRingtone.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { showRingtonePicker(); return true; } }); notificationsCategory.setEnabled(true); // Set the vibrator value, or hide it on devices w/o a vibrator mInboxVibrate = (CheckBoxPreference) findPreference( FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE); if (mInboxVibrate != null) { mInboxVibrate.setChecked( mInboxFolderPreferences.isNotificationVibrateEnabled()); Vibrator vibrator = (Vibrator) mContext.getSystemService( Context.VIBRATOR_SERVICE); if (vibrator.hasVibrator()) { // When the value is changed, update the setting. mInboxVibrate.setOnPreferenceChangeListener(this); } else { // No vibrator present. Remove the preference altogether. notificationsCategory.removePreference(mInboxVibrate); mInboxVibrate = null; } } boolean isArgbNotifColorSupported = getResources().getBoolean( com.android.internal.R.bool.config_multiColorNotificationLed); mInboxLights = (FolderNotificationLightPreference) findPreference( FolderPreferences.PreferenceKeys.NOTIFICATION_LIGHTS); if (mInboxLights != null) { if (isArgbNotifColorSupported) { updateNotificationLight(mInboxFolderPreferences.getNotificationLight()); mInboxLights.setOnPreferenceChangeListener(this); } else { notificationsCategory.removePreference(mInboxLights); } } } } else { notificationsCategory.setEnabled(false); } final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT); final PreferenceCategory policiesCategory = (PreferenceCategory) findPreference( PREFERENCE_CATEGORY_POLICIES); if (policiesCategory != null) { // TODO: This code for showing policies isn't working. For KLP, just don't even bother // showing this data; we'll fix this later. /* if (policy != null) { if (policy.mProtocolPoliciesEnforced != null) { ArrayList policies = getSystemPoliciesList(policy); setPolicyListSummary(policies, policy.mProtocolPoliciesEnforced, PREFERENCE_POLICIES_ENFORCED); } if (policy.mProtocolPoliciesUnsupported != null) { ArrayList policies = new ArrayList(); setPolicyListSummary(policies, policy.mProtocolPoliciesUnsupported, PREFERENCE_POLICIES_UNSUPPORTED); } else { // Don't show "retry" unless we have unsupported policies policiesCategory.removePreference(retryAccount); } } else { */ // Remove the category completely if there are no policies getPreferenceScreen().removePreference(policiesCategory); //} } if (retryAccount != null) { retryAccount.setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { // Release the account SecurityPolicy.setAccountHoldFlag(mContext, mAccount, false); // Remove the preference if (policiesCategory != null) { policiesCategory.removePreference(retryAccount); } return true; } }); } findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { onIncomingSettings(mAccount); return true; } }); // Hide the outgoing account setup link if it's not activated final Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING); if (prefOutgoing != null) { if (mServiceInfo.usesSmtp && mAccount.mHostAuthSend != null) { prefOutgoing.setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { onOutgoingSettings(mAccount); return true; } }); } else { 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(LogUtils.TAG, "Account %d has a bad outbound hostauth", mAccount.getId()); } PreferenceCategory serverCategory = (PreferenceCategory) findPreference( PREFERENCE_CATEGORY_SERVER); serverCategory.removePreference(prefOutgoing); } } 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)); syncContacts.setOnPreferenceChangeListener(this); } else { syncContacts.setChecked(false); syncContacts.setEnabled(false); } if (mServiceInfo.syncCalendar) { syncCalendar.setChecked(ContentResolver .getSyncAutomatically(androidAcct, CalendarContract.AUTHORITY)); syncCalendar.setOnPreferenceChangeListener(this); } else { syncCalendar.setChecked(false); syncCalendar.setEnabled(false); } syncEmail.setChecked(ContentResolver .getSyncAutomatically(androidAcct, EmailContent.AUTHORITY)); syncEmail.setOnPreferenceChangeListener(this); } else { dataUsageCategory.removePreference(syncContacts); dataUsageCategory.removePreference(syncCalendar); dataUsageCategory.removePreference(syncEmail); } } } /** * Shows the system ringtone picker. */ private void showRingtonePicker() { Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri(); if (!TextUtils.isEmpty(ringtoneUri)) { intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(ringtoneUri)); } intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); 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); } private void deleteAccount() { AsyncTask task = new AsyncTask() { private ProgressDialog mDialog; @Override protected void onPreExecute() { // Display an alert dialog to advise the user that the operation is in progress mDialog = new ProgressDialog(mContext); mDialog.setMessage(mContext.getString(R.string.deleting_account_msg)); mDialog.setCancelable(false); mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); mDialog.show(); } @Override protected void onPostExecute(Boolean result) { if (!result) { Toast.makeText(mContext, R.string.delete_account_failed, Toast.LENGTH_SHORT).show(); } mDialog.dismiss(); } @Override protected Boolean doInBackground(Void... params) { try { // Retrieve the necessary information AccountManager accountManager = (AccountManager)mContext.getSystemService( Context.ACCOUNT_SERVICE); android.accounts.Account account = mUiAccount.getAccountManagerAccount(); // Remove the email account and its notifications ContentResolver resolver = mContext.getContentResolver(); int ret = resolver.delete(mUiAccount.uri, null, null); if (ret <= 0) { LogUtils.w(LogUtils.TAG, "Failed to delete account %s", mAccountEmail); return Boolean.FALSE; } NotificationUtils.clearAccountNotifications(mContext, account); // And now we remove the system account that holds the email service accountManager.removeAccount(account, getActivity(), null, null); // Finish after account is deleted getActivity().finish(); } catch (Exception ex) { LogUtils.w(LogUtils.TAG, ex, "Failed to delete account %s", mAccountEmail); return Boolean.FALSE; } return Boolean.TRUE; } }; task.execute(); } private void updateNotificationLight(NotificationLight notificationLight) { if (notificationLight.mOn) { mInboxLights.setColor(notificationLight.mColor); mInboxLights.setOnOffValue(notificationLight.mTimeOn, notificationLight.mTimeOff); } else { int color = mUiAccount != null && mUiAccount.color != 0 ? mUiAccount.color : FolderNotificationLightPreference.DEFAULT_COLOR; mInboxLights.setColor(color); mInboxLights.setOnOffValue(FolderNotificationLightPreference.DEFAULT_TIME, FolderNotificationLightPreference.DEFAULT_TIME); } mInboxLights.setOn(notificationLight.mOn); } private void fillCheckFrecuency(CharSequence[] labels, CharSequence[] values) { if (mCheckFrequency == null) { return; } // Check push capability prior to include as an option if (mAccount != null) { boolean hasPushCapability = mAccount.hasCapability(EmailServiceProxy.CAPABILITY_PUSH); List valuesList = new ArrayList<>(Arrays.asList(values)); int checkIntervalPushPos = valuesList.indexOf( String.valueOf(Account.CHECK_INTERVAL_PUSH)); if (!hasPushCapability && checkIntervalPushPos != -1) { List labelsList = new ArrayList<>(Arrays.asList(labels)); labelsList.remove(checkIntervalPushPos); valuesList.remove(checkIntervalPushPos); labels = labelsList.toArray(new CharSequence[labelsList.size()]); values = valuesList.toArray(new CharSequence[valuesList.size()]); } } mCheckFrequency.setEntries(labels); mCheckFrequency.setEntryValues(values); mCheckFrequency.setDefaultValue(values); } }