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