AccountSettingsFragment.java revision cc0185f07c9198008d8dc685ae9979f3e35e8539
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email.activity.setup;
18
19import android.app.Activity;
20import android.content.ContentResolver;
21import android.content.ContentValues;
22import android.content.Context;
23import android.content.SharedPreferences;
24import android.content.res.Resources;
25import android.os.AsyncTask;
26import android.os.Bundle;
27import android.os.Vibrator;
28import android.preference.CheckBoxPreference;
29import android.preference.EditTextPreference;
30import android.preference.ListPreference;
31import android.preference.Preference;
32import android.preference.PreferenceCategory;
33import android.preference.RingtonePreference;
34import android.provider.CalendarContract;
35import android.provider.ContactsContract;
36import android.text.TextUtils;
37import android.util.Log;
38
39import com.android.email.R;
40import com.android.email.SecurityPolicy;
41import com.android.email.service.EmailServiceUtils;
42import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
43import com.android.email2.ui.MailActivityEmail;
44import com.android.emailcommon.AccountManagerTypes;
45import com.android.emailcommon.Logging;
46import com.android.emailcommon.provider.Account;
47import com.android.emailcommon.provider.EmailContent;
48import com.android.emailcommon.provider.HostAuth;
49import com.android.emailcommon.provider.Policy;
50import com.android.emailcommon.utility.Utility;
51
52import java.util.ArrayList;
53
54/**
55 * Fragment containing the main logic for account settings.  This also calls out to other
56 * fragments for server settings.
57 *
58 * TODO: Remove or make async the mAccountDirty reload logic.  Probably no longer needed.
59 * TODO: Can we defer calling addPreferencesFromResource() until after we load the account?  This
60 *       could reduce flicker.
61 */
62public class AccountSettingsFragment extends EmailPreferenceFragment
63        implements Preference.OnPreferenceChangeListener {
64
65    // Keys used for arguments bundle
66    private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId";
67    private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email";
68
69    public static final String PREFERENCE_DESCRIPTION = "account_description";
70    private static final String PREFERENCE_NAME = "account_name";
71    private static final String PREFERENCE_SIGNATURE = "account_signature";
72    private static final String PREFERENCE_QUICK_RESPONSES = "account_quick_responses";
73    private static final String PREFERENCE_FREQUENCY = "account_check_frequency";
74    private static final String PREFERENCE_BACKGROUND_ATTACHMENTS =
75            "account_background_attachments";
76    private static final String PREFERENCE_DEFAULT = "account_default";
77    private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage";
78    private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications";
79    private static final String PREFERENCE_NOTIFY = "account_notify";
80    private static final String PREFERENCE_VIBRATE_WHEN = "account_settings_vibrate_when";
81    private static final String PREFERENCE_RINGTONE = "account_ringtone";
82    private static final String PREFERENCE_CATEGORY_SERVER = "account_servers";
83    private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies";
84    private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced";
85    private static final String PREFERENCE_POLICIES_UNSUPPORTED = "policies_unsupported";
86    private static final String PREFERENCE_POLICIES_RETRY_ACCOUNT = "policies_retry_account";
87    private static final String PREFERENCE_INCOMING = "incoming";
88    private static final String PREFERENCE_OUTGOING = "outgoing";
89    private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts";
90    private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar";
91    private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email";
92
93    // These strings must match account_settings_vibrate_when_* strings in strings.xml
94    private static final String PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS = "always";
95    private static final String PREFERENCE_VALUE_VIBRATE_WHEN_SILENT = "silent";
96    private static final String PREFERENCE_VALUE_VIBRATE_WHEN_NEVER = "never";
97
98    private EditTextPreference mAccountDescription;
99    private EditTextPreference mAccountName;
100    private EditTextPreference mAccountSignature;
101    private ListPreference mCheckFrequency;
102    private ListPreference mSyncWindow;
103    private CheckBoxPreference mAccountBackgroundAttachments;
104    private CheckBoxPreference mAccountDefault;
105    private CheckBoxPreference mAccountNotify;
106    private ListPreference mAccountVibrateWhen;
107    private RingtonePreference mAccountRingtone;
108    private CheckBoxPreference mSyncContacts;
109    private CheckBoxPreference mSyncCalendar;
110    private CheckBoxPreference mSyncEmail;
111
112    private Context mContext;
113    private Account mAccount;
114    private boolean mAccountDirty;
115    private long mDefaultAccountId;
116    private Callback mCallback = EmptyCallback.INSTANCE;
117    private boolean mStarted;
118    private boolean mLoaded;
119    private boolean mSaveOnExit;
120
121    /** The e-mail of the account being edited. */
122    private String mAccountEmail;
123
124    // Async Tasks
125    private AsyncTask<?,?,?> mLoadAccountTask;
126
127    /**
128     * Callback interface that owning activities must provide
129     */
130    public interface Callback {
131        public void onSettingsChanged(Account account, String preference, Object value);
132        public void onEditQuickResponses(Account account);
133        public void onIncomingSettings(Account account);
134        public void onOutgoingSettings(Account account);
135        public void abandonEdit();
136    }
137
138    private static class EmptyCallback implements Callback {
139        public static final Callback INSTANCE = new EmptyCallback();
140        @Override public void onSettingsChanged(Account account, String preference, Object value) {}
141        @Override public void onEditQuickResponses(Account account) {}
142        @Override public void onIncomingSettings(Account account) {}
143        @Override public void onOutgoingSettings(Account account) {}
144        @Override public void abandonEdit() {}
145    }
146
147    /**
148     * If launching with an arguments bundle, use this method to build the arguments.
149     */
150    public static Bundle buildArguments(long accountId, String email) {
151        Bundle b = new Bundle();
152        b.putLong(BUNDLE_KEY_ACCOUNT_ID, accountId);
153        b.putString(BUNDLE_KEY_ACCOUNT_EMAIL, email);
154        return b;
155    }
156
157    public static String getTitleFromArgs(Bundle args) {
158        return (args == null) ? null : args.getString(BUNDLE_KEY_ACCOUNT_EMAIL);
159    }
160
161    @Override
162    public void onAttach(Activity activity) {
163        super.onAttach(activity);
164        mContext = activity;
165    }
166
167    /**
168     * Called to do initial creation of a fragment.  This is called after
169     * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
170     */
171    @Override
172    public void onCreate(Bundle savedInstanceState) {
173        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
174            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onCreate");
175        }
176        super.onCreate(savedInstanceState);
177
178        // Load the preferences from an XML resource
179        addPreferencesFromResource(R.xml.account_settings_preferences);
180
181        // Start loading the account data, if provided in the arguments
182        // If not, activity must call startLoadingAccount() directly
183        Bundle b = getArguments();
184        if (b != null) {
185            long accountId = b.getLong(BUNDLE_KEY_ACCOUNT_ID, -1);
186            mAccountEmail = b.getString(BUNDLE_KEY_ACCOUNT_EMAIL);
187            if (accountId >= 0 && !mLoaded) {
188                startLoadingAccount(accountId);
189            }
190        }
191
192        mAccountDirty = false;
193    }
194
195    @Override
196    public void onActivityCreated(Bundle savedInstanceState) {
197        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
198            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onActivityCreated");
199        }
200        super.onActivityCreated(savedInstanceState);
201    }
202
203    /**
204     * Called when the Fragment is visible to the user.
205     */
206    @Override
207    public void onStart() {
208        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
209            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStart");
210        }
211        super.onStart();
212        mStarted = true;
213
214        // If the loaded account is ready now, load the UI
215        if (mAccount != null && !mLoaded) {
216            loadSettings();
217        }
218    }
219
220    /**
221     * Called when the fragment is visible to the user and actively running.
222     * TODO: Don't read account data on UI thread.  This should be fixed by removing the need
223     * to do this, not by spinning up yet another thread.
224     */
225    @Override
226    public void onResume() {
227        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
228            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onResume");
229        }
230        super.onResume();
231
232        if (mAccountDirty) {
233            // if we are coming back from editing incoming or outgoing settings,
234            // we need to refresh them here so we don't accidentally overwrite the
235            // old values we're still holding here
236            mAccount.mHostAuthRecv =
237                HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
238            mAccount.mHostAuthSend =
239                HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeySend);
240            // Because "delete policy" UI is on edit incoming settings, we have
241            // to refresh that as well.
242            Account refreshedAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
243            if (refreshedAccount == null || mAccount.mHostAuthRecv == null
244                    || mAccount.mHostAuthSend == null) {
245                mSaveOnExit = false;
246                mCallback.abandonEdit();
247                return;
248            }
249            mAccount.setDeletePolicy(refreshedAccount.getDeletePolicy());
250            mAccountDirty = false;
251        }
252    }
253
254    @Override
255    public void onPause() {
256        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
257            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onPause");
258        }
259        super.onPause();
260        if (mSaveOnExit) {
261            saveSettings();
262        }
263    }
264
265    /**
266     * Called when the Fragment is no longer started.
267     */
268    @Override
269    public void onStop() {
270        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
271            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStop");
272        }
273        super.onStop();
274        mStarted = false;
275    }
276
277    /**
278     * Listen to all preference changes in this class.
279     * @param preference
280     * @param newValue
281     * @return
282     */
283    @Override
284    public boolean onPreferenceChange(Preference preference, Object newValue){
285        // Can't use a switch here. Falling back to a giant conditional.
286        final String key = preference.getKey();
287        if (key.equals(PREFERENCE_DESCRIPTION)){
288            String summary = newValue.toString().trim();
289            if (TextUtils.isEmpty(summary)) {
290                summary = mAccount.mEmailAddress;
291            }
292            mAccountDescription.setSummary(summary);
293            mAccountDescription.setText(summary);
294            preferenceChanged(PREFERENCE_DESCRIPTION, summary);
295            return false;
296        } else if (key.equals(PREFERENCE_FREQUENCY)) {
297            final String summary = newValue.toString();
298            final int index = mCheckFrequency.findIndexOfValue(summary);
299            mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]);
300            mCheckFrequency.setValue(summary);
301            preferenceChanged(PREFERENCE_FREQUENCY, newValue);
302            return false;
303        } else if (key.equals(PREFERENCE_SIGNATURE)) {
304            // Clean up signature if it's only whitespace (which is easy to do on a
305            // soft keyboard) but leave whitespace in place otherwise, to give the user
306            // maximum flexibility, e.g. the ability to indent
307            String signature = newValue.toString();
308            if (signature.trim().isEmpty()) {
309                signature = "";
310            }
311            mAccountSignature.setText(signature);
312            preferenceChanged(PREFERENCE_SIGNATURE, signature);
313            return false;
314        } else if (key.equals(PREFERENCE_NAME)) {
315            final String summary = newValue.toString().trim();
316            if (!TextUtils.isEmpty(summary)) {
317                mAccountName.setSummary(summary);
318                mAccountName.setText(summary);
319                preferenceChanged(PREFERENCE_NAME, summary);
320            }
321            return false;
322        } else if (key.equals(PREFERENCE_VIBRATE_WHEN)) {
323            final String vibrateSetting = newValue.toString();
324            final int index = mAccountVibrateWhen.findIndexOfValue(vibrateSetting);
325            mAccountVibrateWhen.setSummary(mAccountVibrateWhen.getEntries()[index]);
326            mAccountVibrateWhen.setValue(vibrateSetting);
327            preferenceChanged(PREFERENCE_VIBRATE_WHEN, newValue);
328            return false;
329        } else {
330            // Default behavior, just indicate that the preferences were written
331            preferenceChanged(key, newValue);
332            return true;
333        }
334    }
335
336    /**
337     * Called when the fragment is no longer in use.
338     */
339    @Override
340    public void onDestroy() {
341        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
342            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy");
343        }
344        super.onDestroy();
345
346        Utility.cancelTaskInterrupt(mLoadAccountTask);
347        mLoadAccountTask = null;
348    }
349
350    @Override
351    public void onSaveInstanceState(Bundle outState) {
352        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
353            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState");
354        }
355        super.onSaveInstanceState(outState);
356    }
357
358    /**
359     * Activity provides callbacks here
360     */
361    public void setCallback(Callback callback) {
362        mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
363    }
364
365    /**
366     * Start loading a single account in preparation for editing it
367     */
368    public void startLoadingAccount(long accountId) {
369        Utility.cancelTaskInterrupt(mLoadAccountTask);
370        mLoadAccountTask = new LoadAccountTask().executeOnExecutor(
371                AsyncTask.THREAD_POOL_EXECUTOR, accountId);
372    }
373
374    /**
375     * Async task to load account in order to view/edit it
376     */
377    private class LoadAccountTask extends AsyncTask<Long, Void, Object[]> {
378        @Override
379        protected Object[] doInBackground(Long... params) {
380            long accountId = params[0];
381            Account account = Account.restoreAccountWithId(mContext, accountId);
382            if (account != null) {
383                account.mHostAuthRecv =
384                    HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv);
385                account.mHostAuthSend =
386                    HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend);
387                if (account.mHostAuthRecv == null || account.mHostAuthSend == null) {
388                    account = null;
389                }
390            }
391            long defaultAccountId = Account.getDefaultAccountId(mContext);
392            return new Object[] { account, Long.valueOf(defaultAccountId) };
393        }
394
395        @Override
396        protected void onPostExecute(Object[] results) {
397            if (results != null && !isCancelled()) {
398                Account account = (Account) results[0];
399                if (account == null) {
400                    mSaveOnExit = false;
401                    mCallback.abandonEdit();
402                } else {
403                    mAccount = account;
404                    mDefaultAccountId = (Long) results[1];
405                    if (mStarted && !mLoaded) {
406                        loadSettings();
407                    }
408                }
409            }
410        }
411    }
412
413    /**
414     * From a Policy, create and return an ArrayList of Strings that describe (simply) those
415     * policies that are supported by the OS.  At the moment, the strings are simple (e.g.
416     * "password required"); we should probably add more information (# characters, etc.), though
417     */
418    private ArrayList<String> getSystemPoliciesList(Policy policy) {
419        Resources res = mContext.getResources();
420        ArrayList<String> policies = new ArrayList<String>();
421        if (policy.mPasswordMode != Policy.PASSWORD_MODE_NONE) {
422            policies.add(res.getString(R.string.policy_require_password));
423        }
424        if (policy.mPasswordHistory > 0) {
425            policies.add(res.getString(R.string.policy_password_history));
426        }
427        if (policy.mPasswordExpirationDays > 0) {
428            policies.add(res.getString(R.string.policy_password_expiration));
429        }
430        if (policy.mMaxScreenLockTime > 0) {
431            policies.add(res.getString(R.string.policy_screen_timeout));
432        }
433        if (policy.mDontAllowCamera) {
434            policies.add(res.getString(R.string.policy_dont_allow_camera));
435        }
436        if (policy.mMaxEmailLookback != 0) {
437            policies.add(res.getString(R.string.policy_email_age));
438        }
439        if (policy.mMaxCalendarLookback != 0) {
440            policies.add(res.getString(R.string.policy_calendar_age));
441        }
442        return policies;
443    }
444
445    private void setPolicyListSummary(ArrayList<String> policies, String policiesToAdd,
446            String preferenceName) {
447        Policy.addPolicyStringToList(policiesToAdd, policies);
448        if (policies.size() > 0) {
449            Preference p = findPreference(preferenceName);
450            StringBuilder sb = new StringBuilder();
451            for (String desc: policies) {
452                sb.append(desc);
453                sb.append('\n');
454            }
455            p.setSummary(sb.toString());
456        }
457    }
458
459    /**
460     * Load account data into preference UI
461     */
462    private void loadSettings() {
463        // We can only do this once, so prevent repeat
464        mLoaded = true;
465        // Once loaded the data is ready to be saved, as well
466        mSaveOnExit = false;
467
468        mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION);
469        mAccountDescription.setSummary(mAccount.getDisplayName());
470        mAccountDescription.setText(mAccount.getDisplayName());
471        mAccountDescription.setOnPreferenceChangeListener(this);
472
473        mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME);
474        String senderName = mAccount.getSenderName();
475        // In rare cases, sendername will be null;  Change this to empty string to avoid NPE's
476        if (senderName == null) senderName = "";
477        mAccountName.setSummary(senderName);
478        mAccountName.setText(senderName);
479        mAccountName.setOnPreferenceChangeListener(this);
480
481        mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE);
482        mAccountSignature.setText(mAccount.getSignature());
483        mAccountSignature.setOnPreferenceChangeListener(this);
484
485        mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
486        String protocol = Account.getProtocol(mContext, mAccount.mId);
487        EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext, protocol);
488        mCheckFrequency.setEntries(info.syncIntervalStrings);
489        mCheckFrequency.setEntryValues(info.syncIntervals);
490        mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval()));
491        mCheckFrequency.setSummary(mCheckFrequency.getEntry());
492        mCheckFrequency.setOnPreferenceChangeListener(this);
493
494        findPreference(PREFERENCE_QUICK_RESPONSES).setOnPreferenceClickListener(
495                new Preference.OnPreferenceClickListener() {
496                    @Override
497                    public boolean onPreferenceClick(Preference preference) {
498                        mAccountDirty = true;
499                        mCallback.onEditQuickResponses(mAccount);
500                        return true;
501                    }
502                });
503
504        // Add check window preference
505        PreferenceCategory dataUsageCategory =
506                (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE);
507
508        mSyncWindow = null;
509        if (info.offerLookback) {
510            mSyncWindow = new ListPreference(mContext);
511            mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label);
512            mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback()));
513            mSyncWindow.setSummary(mSyncWindow.getEntry());
514            MailboxSettings.setupLookbackPreferenceOptions(mContext, mSyncWindow, mAccount);
515
516            // Must correspond to the hole in the XML file that's reserved.
517            mSyncWindow.setOrder(2);
518            mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
519                @Override
520                public boolean onPreferenceChange(Preference preference, Object newValue) {
521                    final String summary = newValue.toString();
522                    int index = mSyncWindow.findIndexOfValue(summary);
523                    mSyncWindow.setSummary(mSyncWindow.getEntries()[index]);
524                    mSyncWindow.setValue(summary);
525                    preferenceChanged(preference.getKey(), newValue);
526                    return false;
527                }
528            });
529            dataUsageCategory.addPreference(mSyncWindow);
530        }
531
532        mAccountBackgroundAttachments = (CheckBoxPreference)
533                findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS);
534        if (!info.offerAttachmentPreload) {
535            dataUsageCategory.removePreference(mAccountBackgroundAttachments);
536        } else {
537            mAccountBackgroundAttachments.setChecked(
538                    0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS));
539            mAccountBackgroundAttachments.setOnPreferenceChangeListener(this);
540        }
541
542        mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT);
543        mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId);
544        mAccountDefault.setOnPreferenceChangeListener(this);
545
546        mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY);
547        mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL));
548        mAccountNotify.setOnPreferenceChangeListener(this);
549
550        mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE);
551        mAccountRingtone.setOnPreferenceChangeListener(this);
552
553        // The following two lines act as a workaround for the RingtonePreference
554        // which does not let us set/get the value programmatically
555        SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
556        prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply();
557
558        // Set the vibrator value, or hide it on devices w/o a vibrator
559        mAccountVibrateWhen = (ListPreference) findPreference(PREFERENCE_VIBRATE_WHEN);
560        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
561        if (vibrator.hasVibrator()) {
562            // Calculate the value to set based on the choices, and set the value.
563            final boolean vibrateAlways = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_ALWAYS);
564            final boolean vibrateWhenSilent =
565                    0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_WHEN_SILENT);
566            final String vibrateSetting =
567                    vibrateAlways ? PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS :
568                        vibrateWhenSilent ? PREFERENCE_VALUE_VIBRATE_WHEN_SILENT :
569                            PREFERENCE_VALUE_VIBRATE_WHEN_NEVER;
570            mAccountVibrateWhen.setValue(vibrateSetting);
571
572            // Update the summary string.
573            final int index = mAccountVibrateWhen.findIndexOfValue(vibrateSetting);
574            mAccountVibrateWhen.setSummary(mAccountVibrateWhen.getEntries()[index]);
575
576            // When the value is changed, update the summary in addition to the setting.
577            mAccountVibrateWhen.setOnPreferenceChangeListener(this);
578        } else {
579            // No vibrator present. Remove the preference altogether.
580            PreferenceCategory notificationsCategory = (PreferenceCategory)
581                    findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS);
582            notificationsCategory.removePreference(mAccountVibrateWhen);
583        }
584
585        final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT);
586        final PreferenceCategory policiesCategory = (PreferenceCategory) findPreference(
587                PREFERENCE_CATEGORY_POLICIES);
588        if (mAccount.mPolicyKey > 0) {
589            // Make sure we have most recent data from account
590            mAccount.refresh(mContext);
591            Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey);
592            if (policy == null) {
593                // The account has been deleted?  Crazy, but not impossible
594                return;
595            }
596            if (policy.mProtocolPoliciesEnforced != null) {
597                ArrayList<String> policies = getSystemPoliciesList(policy);
598                setPolicyListSummary(policies, policy.mProtocolPoliciesEnforced,
599                        PREFERENCE_POLICIES_ENFORCED);
600            }
601            if (policy.mProtocolPoliciesUnsupported != null) {
602                ArrayList<String> policies = new ArrayList<String>();
603                setPolicyListSummary(policies, policy.mProtocolPoliciesUnsupported,
604                        PREFERENCE_POLICIES_UNSUPPORTED);
605            } else {
606                // Don't show "retry" unless we have unsupported policies
607                policiesCategory.removePreference(retryAccount);
608            }
609        } else {
610            // Remove the category completely if there are no policies
611            getPreferenceScreen().removePreference(policiesCategory);
612        }
613
614        retryAccount.setOnPreferenceClickListener(
615                new Preference.OnPreferenceClickListener() {
616                    @Override
617                    public boolean onPreferenceClick(Preference preference) {
618                        // Release the account
619                        SecurityPolicy.setAccountHoldFlag(mContext, mAccount, false);
620                        // Remove the preference
621                        policiesCategory.removePreference(retryAccount);
622                        return true;
623                    }
624                });
625        findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener(
626                new Preference.OnPreferenceClickListener() {
627                    @Override
628                    public boolean onPreferenceClick(Preference preference) {
629                        mAccountDirty = true;
630                        mCallback.onIncomingSettings(mAccount);
631                        return true;
632                    }
633                });
634
635        // Hide the outgoing account setup link if it's not activated
636        Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING);
637        if (info.usesSmtp) {
638            prefOutgoing.setOnPreferenceClickListener(
639                    new Preference.OnPreferenceClickListener() {
640                        @Override
641                        public boolean onPreferenceClick(Preference preference) {
642                            mAccountDirty = true;
643                            mCallback.onOutgoingSettings(mAccount);
644                            return true;
645                        }
646                    });
647        } else {
648            PreferenceCategory serverCategory = (PreferenceCategory) findPreference(
649                    PREFERENCE_CATEGORY_SERVER);
650            serverCategory.removePreference(prefOutgoing);
651        }
652
653        mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS);
654        mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR);
655        mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL);
656        if (info.syncContacts || info.syncCalendar) {
657            android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
658                    info.accountType);
659            if (info.syncContacts) {
660                mSyncContacts.setChecked(ContentResolver
661                        .getSyncAutomatically(acct, ContactsContract.AUTHORITY));
662                mSyncContacts.setOnPreferenceChangeListener(this);
663            } else {
664                mSyncContacts.setChecked(false);
665                mSyncContacts.setEnabled(false);
666            }
667            if (info.syncCalendar) {
668                mSyncCalendar.setChecked(ContentResolver
669                        .getSyncAutomatically(acct, CalendarContract.AUTHORITY));
670                mSyncCalendar.setOnPreferenceChangeListener(this);
671            } else {
672                mSyncCalendar.setChecked(false);
673                mSyncCalendar.setEnabled(false);
674            }
675            mSyncEmail.setChecked(ContentResolver
676                    .getSyncAutomatically(acct, EmailContent.AUTHORITY));
677            mSyncEmail.setOnPreferenceChangeListener(this);
678        } else {
679            dataUsageCategory.removePreference(mSyncContacts);
680            dataUsageCategory.removePreference(mSyncCalendar);
681            dataUsageCategory.removePreference(mSyncEmail);
682        }
683    }
684
685    /**
686     * Called any time a preference is changed.
687     */
688    private void preferenceChanged(String preference, Object value) {
689        mCallback.onSettingsChanged(mAccount, preference, value);
690        mSaveOnExit = true;
691    }
692
693    /*
694     * Note: This writes the settings on the UI thread.  This has to be done so the settings are
695     * committed before we might be killed.
696     */
697    private void saveSettings() {
698        // Turn off all controlled flags - will turn them back on while checking UI elements
699        int newFlags = mAccount.getFlags() &
700                ~(Account.FLAGS_NOTIFY_NEW_MAIL |
701                        Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_VIBRATE_WHEN_SILENT |
702                        Account.FLAGS_BACKGROUND_ATTACHMENTS);
703
704        newFlags |= mAccountBackgroundAttachments.isChecked() ?
705                Account.FLAGS_BACKGROUND_ATTACHMENTS : 0;
706        mAccount.setDefaultAccount(mAccountDefault.isChecked());
707        // If the display name has been cleared, we'll reset it to the default value (email addr)
708        mAccount.setDisplayName(mAccountDescription.getText().trim());
709        // The sender name must never be empty (this is enforced by the preference editor)
710        mAccount.setSenderName(mAccountName.getText().trim());
711        mAccount.setSignature(mAccountSignature.getText());
712        newFlags |= mAccountNotify.isChecked() ? Account.FLAGS_NOTIFY_NEW_MAIL : 0;
713        mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue()));
714        if (mSyncWindow != null) {
715            mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue()));
716        }
717        if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS)) {
718            newFlags |= Account.FLAGS_VIBRATE_ALWAYS;
719        } else if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_SILENT)) {
720            newFlags |= Account.FLAGS_VIBRATE_WHEN_SILENT;
721        }
722        SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
723        mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));
724        mAccount.setFlags(newFlags);
725
726        EmailServiceInfo info =
727                EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext));
728        if (info.syncContacts || info.syncCalendar) {
729            android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
730                    AccountManagerTypes.TYPE_EXCHANGE);
731            ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY,
732                    mSyncContacts.isChecked());
733            ContentResolver.setSyncAutomatically(acct, CalendarContract.AUTHORITY,
734                    mSyncCalendar.isChecked());
735            ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY,
736                    mSyncEmail.isChecked());
737        }
738
739        // Commit the changes
740        // Note, this is done in the UI thread because at this point, we must commit
741        // all changes - any time after onPause completes, we could be killed.  This is analogous
742        // to the way that SharedPreferences tries to work off-thread in apply(), but will pause
743        // until completion in onPause().
744        ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount);
745        mAccount.update(mContext, cv);
746
747        // Run the remaining changes off-thread
748        MailActivityEmail.setServicesEnabledAsync(mContext);
749    }
750
751    public String getAccountEmail() {
752        // Get the e-mail address of the account being editted, if this is for an existing account.
753        return mAccountEmail;
754    }
755}
756