AccountSettingsFragment.java revision 82e0e1c4c5c3d526823cf3d8d69bf12c1d44a06a
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.app.AlertDialog;
21import android.app.Dialog;
22import android.app.DialogFragment;
23import android.app.Fragment;
24import android.app.FragmentTransaction;
25import android.content.ContentResolver;
26import android.content.ContentValues;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.SharedPreferences;
30import android.os.AsyncTask;
31import android.os.Bundle;
32import android.os.Vibrator;
33import android.preference.CheckBoxPreference;
34import android.preference.EditTextPreference;
35import android.preference.ListPreference;
36import android.preference.Preference;
37import android.preference.PreferenceCategory;
38import android.preference.PreferenceFragment;
39import android.preference.RingtonePreference;
40import android.provider.ContactsContract;
41import android.text.TextUtils;
42import android.util.Log;
43
44import com.android.email.Email;
45import com.android.email.R;
46import com.android.email.mail.Sender;
47import com.android.emailcommon.AccountManagerTypes;
48import com.android.emailcommon.CalendarProviderStub;
49import com.android.emailcommon.Logging;
50import com.android.emailcommon.mail.MessagingException;
51import com.android.emailcommon.provider.Account;
52import com.android.emailcommon.provider.EmailContent;
53import com.android.emailcommon.provider.HostAuth;
54import com.android.emailcommon.utility.Utility;
55
56/**
57 * Fragment containing the main logic for account settings.  This also calls out to other
58 * fragments for server settings.
59 *
60 * TODO: Remove or make async the mAccountDirty reload logic.  Probably no longer needed.
61 * TODO: Can we defer calling addPreferencesFromResource() until after we load the account?  This
62 *       could reduce flicker.
63 */
64public class AccountSettingsFragment extends PreferenceFragment {
65
66    // Keys used for arguments bundle
67    private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId";
68    private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email";
69
70    private static final String PREFERENCE_CATEGORY_TOP = "account_settings";
71    public static final String PREFERENCE_DESCRIPTION = "account_description";
72    private static final String PREFERENCE_NAME = "account_name";
73    private static final String PREFERENCE_SIGNATURE = "account_signature";
74    private static final String PREFERENCE_FREQUENCY = "account_check_frequency";
75    private static final String PREFERENCE_BACKGROUND_ATTACHMENTS =
76            "account_background_attachments";
77    private static final String PREFERENCE_DEFAULT = "account_default";
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_INCOMING = "incoming";
84    private static final String PREFERENCE_OUTGOING = "outgoing";
85    private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts";
86    private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar";
87    private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email";
88    private static final String PREFERENCE_DELETE_ACCOUNT = "delete_account";
89
90    // These strings must match account_settings_vibrate_when_* strings in strings.xml
91    private static final String PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS = "always";
92    private static final String PREFERENCE_VALUE_VIBRATE_WHEN_SILENT = "silent";
93    private static final String PREFERENCE_VALUE_VIBRATE_WHEN_NEVER = "never";
94
95    private EditTextPreference mAccountDescription;
96    private EditTextPreference mAccountName;
97    private EditTextPreference mAccountSignature;
98    private ListPreference mCheckFrequency;
99    private ListPreference mSyncWindow;
100    private CheckBoxPreference mAccountBackgroundAttachments;
101    private CheckBoxPreference mAccountDefault;
102    private CheckBoxPreference mAccountNotify;
103    private ListPreference mAccountVibrateWhen;
104    private RingtonePreference mAccountRingtone;
105    private CheckBoxPreference mSyncContacts;
106    private CheckBoxPreference mSyncCalendar;
107    private CheckBoxPreference mSyncEmail;
108
109    private Context mContext;
110    private Account mAccount;
111    private boolean mAccountDirty;
112    private long mDefaultAccountId;
113    private Callback mCallback = EmptyCallback.INSTANCE;
114    private boolean mStarted;
115    private boolean mLoaded;
116    private boolean mSaveOnExit;
117
118    /** The e-mail of the account being edited. */
119    private String mAccountEmail;
120
121    // Async Tasks
122    private AsyncTask<?,?,?> mLoadAccountTask;
123
124    /**
125     * Callback interface that owning activities must provide
126     */
127    public interface Callback {
128        public void onSettingsChanged(Account account, String preference, Object value);
129        public void onIncomingSettings(Account account);
130        public void onOutgoingSettings(Account account);
131        public void abandonEdit();
132        public void deleteAccount(Account account);
133    }
134
135    private static class EmptyCallback implements Callback {
136        public static final Callback INSTANCE = new EmptyCallback();
137        @Override public void onSettingsChanged(Account account, String preference, Object value) {}
138        @Override public void onIncomingSettings(Account account) {}
139        @Override public void onOutgoingSettings(Account account) {}
140        @Override public void abandonEdit() {}
141        @Override public void deleteAccount(Account account) {}
142    }
143
144    /**
145     * If launching with an arguments bundle, use this method to build the arguments.
146     */
147    public static Bundle buildArguments(long accountId, String email) {
148        Bundle b = new Bundle();
149        b.putLong(BUNDLE_KEY_ACCOUNT_ID, accountId);
150        b.putString(BUNDLE_KEY_ACCOUNT_EMAIL, email);
151        return b;
152    }
153
154    public static String getTitleFromArgs(Bundle args) {
155        return (args == null) ? null : args.getString(BUNDLE_KEY_ACCOUNT_EMAIL);
156    }
157
158    @Override
159    public void onAttach(Activity activity) {
160        super.onAttach(activity);
161        mContext = activity;
162    }
163
164    /**
165     * Called to do initial creation of a fragment.  This is called after
166     * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
167     */
168    @Override
169    public void onCreate(Bundle savedInstanceState) {
170        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
171            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onCreate");
172        }
173        super.onCreate(savedInstanceState);
174
175        // Load the preferences from an XML resource
176        addPreferencesFromResource(R.xml.account_settings_preferences);
177
178        // Start loading the account data, if provided in the arguments
179        // If not, activity must call startLoadingAccount() directly
180        Bundle b = getArguments();
181        if (b != null) {
182            long accountId = b.getLong(BUNDLE_KEY_ACCOUNT_ID, -1);
183            mAccountEmail = b.getString(BUNDLE_KEY_ACCOUNT_EMAIL);
184            if (accountId >= 0 && !mLoaded) {
185                startLoadingAccount(accountId);
186            }
187        }
188
189        mAccountDirty = false;
190    }
191
192    @Override
193    public void onActivityCreated(Bundle savedInstanceState) {
194        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
195            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onActivityCreated");
196        }
197        super.onActivityCreated(savedInstanceState);
198    }
199
200    /**
201     * Called when the Fragment is visible to the user.
202     */
203    @Override
204    public void onStart() {
205        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
206            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStart");
207        }
208        super.onStart();
209        mStarted = true;
210
211        // If the loaded account is ready now, load the UI
212        if (mAccount != null && !mLoaded) {
213            loadSettings();
214        }
215    }
216
217    /**
218     * Called when the fragment is visible to the user and actively running.
219     * TODO: Don't read account data on UI thread.  This should be fixed by removing the need
220     * to do this, not by spinning up yet another thread.
221     */
222    @Override
223    public void onResume() {
224        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
225            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onResume");
226        }
227        super.onResume();
228
229        if (mAccountDirty) {
230            // if we are coming back from editing incoming or outgoing settings,
231            // we need to refresh them here so we don't accidentally overwrite the
232            // old values we're still holding here
233            mAccount.mHostAuthRecv =
234                HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
235            mAccount.mHostAuthSend =
236                HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeySend);
237            // Because "delete policy" UI is on edit incoming settings, we have
238            // to refresh that as well.
239            Account refreshedAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
240            if (refreshedAccount == null || mAccount.mHostAuthRecv == null
241                    || mAccount.mHostAuthSend == null) {
242                mSaveOnExit = false;
243                mCallback.abandonEdit();
244                return;
245            }
246            mAccount.setDeletePolicy(refreshedAccount.getDeletePolicy());
247            mAccountDirty = false;
248        }
249    }
250
251    @Override
252    public void onPause() {
253        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
254            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onPause");
255        }
256        super.onPause();
257        if (mSaveOnExit) {
258            saveSettings();
259        }
260    }
261
262    /**
263     * Called when the Fragment is no longer started.
264     */
265    @Override
266    public void onStop() {
267        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
268            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStop");
269        }
270        super.onStop();
271        mStarted = false;
272    }
273
274    /**
275     * Called when the fragment is no longer in use.
276     */
277    @Override
278    public void onDestroy() {
279        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
280            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy");
281        }
282        super.onDestroy();
283
284        Utility.cancelTaskInterrupt(mLoadAccountTask);
285        mLoadAccountTask = null;
286    }
287
288    @Override
289    public void onSaveInstanceState(Bundle outState) {
290        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
291            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState");
292        }
293        super.onSaveInstanceState(outState);
294    }
295
296    /**
297     * Activity provides callbacks here
298     */
299    public void setCallback(Callback callback) {
300        mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
301    }
302
303    /**
304     * Start loading a single account in preparation for editing it
305     */
306    public void startLoadingAccount(long accountId) {
307        Utility.cancelTaskInterrupt(mLoadAccountTask);
308        mLoadAccountTask = new LoadAccountTask().execute(accountId);
309    }
310
311    /**
312     * Async task to load account in order to view/edit it
313     */
314    private class LoadAccountTask extends AsyncTask<Long, Void, Object[]> {
315        @Override
316        protected Object[] doInBackground(Long... params) {
317            long accountId = params[0];
318            Account account = Account.restoreAccountWithId(mContext, accountId);
319            if (account != null) {
320                account.mHostAuthRecv =
321                    HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv);
322                account.mHostAuthSend =
323                    HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend);
324                if (account.mHostAuthRecv == null || account.mHostAuthSend == null) {
325                    account = null;
326                }
327            }
328            long defaultAccountId = Account.getDefaultAccountId(mContext);
329            return new Object[] { account, Long.valueOf(defaultAccountId) };
330        }
331
332        @Override
333        protected void onPostExecute(Object[] results) {
334            if (results != null && !isCancelled()) {
335                Account account = (Account) results[0];
336                if (account == null) {
337                    mSaveOnExit = false;
338                    mCallback.abandonEdit();
339                } else {
340                    mAccount = account;
341                    mDefaultAccountId = (Long) results[1];
342                    if (mStarted && !mLoaded) {
343                        loadSettings();
344                    }
345                }
346            }
347        }
348    }
349
350    /**
351     * Load account data into preference UI
352     */
353    private void loadSettings() {
354        // We can only do this once, so prevent repeat
355        mLoaded = true;
356        // Once loaded the data is ready to be saved, as well
357        mSaveOnExit = false;
358
359        PreferenceCategory topCategory =
360            (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_TOP);
361        topCategory.setTitle(mContext.getString(R.string.account_settings_title_fmt));
362
363        mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION);
364        mAccountDescription.setSummary(mAccount.getDisplayName());
365        mAccountDescription.setText(mAccount.getDisplayName());
366        mAccountDescription.setOnPreferenceChangeListener(
367            new Preference.OnPreferenceChangeListener() {
368                public boolean onPreferenceChange(Preference preference, Object newValue) {
369                    String summary = newValue.toString().trim();
370                    if (TextUtils.isEmpty(summary)) {
371                        summary = mAccount.mEmailAddress;
372                    }
373                    mAccountDescription.setSummary(summary);
374                    mAccountDescription.setText(summary);
375                    onPreferenceChanged(PREFERENCE_DESCRIPTION, summary);
376                    return false;
377                }
378            }
379        );
380
381        mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME);
382        String senderName = mAccount.getSenderName();
383        // In rare cases, sendername will be null;  Change this to empty string to avoid NPE's
384        if (senderName == null) senderName = "";
385        mAccountName.setSummary(senderName);
386        mAccountName.setText(senderName);
387        mAccountName.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
388            public boolean onPreferenceChange(Preference preference, Object newValue) {
389                final String summary = newValue.toString().trim();
390                if (!TextUtils.isEmpty(summary)) {
391                    mAccountName.setSummary(summary);
392                    mAccountName.setText(summary);
393                    onPreferenceChanged(PREFERENCE_NAME, summary);
394                }
395                return false;
396            }
397        });
398
399        mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE);
400        String signature = mAccount.getSignature();
401        mAccountSignature.setText(mAccount.getSignature());
402        mAccountSignature.setOnPreferenceChangeListener(
403            new Preference.OnPreferenceChangeListener() {
404                public boolean onPreferenceChange(Preference preference, Object newValue) {
405                    // Clean up signature if it's only whitespace (which is easy to do on a
406                    // soft keyboard) but leave whitespace in place otherwise, to give the user
407                    // maximum flexibility, e.g. the ability to indent
408                    String signature = newValue.toString();
409                    if (signature.trim().isEmpty()) {
410                        signature = "";
411                    }
412                    mAccountSignature.setText(signature);
413                    onPreferenceChanged(PREFERENCE_SIGNATURE, signature);
414                    return false;
415                }
416            });
417
418        mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
419
420        // TODO Move protocol into Account to avoid retrieving the HostAuth (implicitly)
421        String protocol = Account.getProtocol(mContext, mAccount.mId);
422        if (HostAuth.SCHEME_EAS.equals(protocol)) {
423            mCheckFrequency.setEntries(R.array.account_settings_check_frequency_entries_push);
424            mCheckFrequency.setEntryValues(R.array.account_settings_check_frequency_values_push);
425        }
426
427        mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval()));
428        mCheckFrequency.setSummary(mCheckFrequency.getEntry());
429        mCheckFrequency.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
430            public boolean onPreferenceChange(Preference preference, Object newValue) {
431                final String summary = newValue.toString();
432                int index = mCheckFrequency.findIndexOfValue(summary);
433                mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]);
434                mCheckFrequency.setValue(summary);
435                onPreferenceChanged(PREFERENCE_FREQUENCY, newValue);
436                return false;
437            }
438        });
439
440        // Add check window preference
441        mSyncWindow = null;
442        if (HostAuth.SCHEME_EAS.equals(protocol)) {
443            mSyncWindow = new ListPreference(mContext);
444            mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label);
445            mSyncWindow.setEntries(R.array.account_settings_mail_window_entries);
446            mSyncWindow.setEntryValues(R.array.account_settings_mail_window_values);
447            mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback()));
448            mSyncWindow.setSummary(mSyncWindow.getEntry());
449            mSyncWindow.setOrder(4);
450            mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
451                public boolean onPreferenceChange(Preference preference, Object newValue) {
452                    final String summary = newValue.toString();
453                    int index = mSyncWindow.findIndexOfValue(summary);
454                    mSyncWindow.setSummary(mSyncWindow.getEntries()[index]);
455                    mSyncWindow.setValue(summary);
456                    onPreferenceChanged(preference.getKey(), newValue);
457                    return false;
458                }
459            });
460            topCategory.addPreference(mSyncWindow);
461        }
462
463        // Show "background attachments" for IMAP & EAS - hide it for POP3.
464        mAccountBackgroundAttachments = (CheckBoxPreference)
465                findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS);
466        if (HostAuth.SCHEME_POP3.equals(mAccount.mHostAuthRecv.mProtocol)) {
467            topCategory.removePreference(mAccountBackgroundAttachments);
468        } else {
469            mAccountBackgroundAttachments.setChecked(
470                    0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS));
471            mAccountBackgroundAttachments.setOnPreferenceChangeListener(mPreferenceChangeListener);
472        }
473
474        mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT);
475        mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId);
476        mAccountDefault.setOnPreferenceChangeListener(mPreferenceChangeListener);
477
478        mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY);
479        mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL));
480        mAccountNotify.setOnPreferenceChangeListener(mPreferenceChangeListener);
481
482        mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE);
483        mAccountRingtone.setOnPreferenceChangeListener(mPreferenceChangeListener);
484
485        // The following two lines act as a workaround for the RingtonePreference
486        // which does not let us set/get the value programmatically
487        SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
488        prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply();
489
490        // Set the vibrator value, or hide it on devices w/o a vibrator
491        mAccountVibrateWhen = (ListPreference) findPreference(PREFERENCE_VIBRATE_WHEN);
492        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
493        if (vibrator.hasVibrator()) {
494            boolean flagsVibrate = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_ALWAYS);
495            boolean flagsVibrateSilent =
496                    0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_WHEN_SILENT);
497            mAccountVibrateWhen.setValue(
498                    flagsVibrate ? PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS :
499                    flagsVibrateSilent ? PREFERENCE_VALUE_VIBRATE_WHEN_SILENT :
500                        PREFERENCE_VALUE_VIBRATE_WHEN_NEVER);
501            mAccountVibrateWhen.setOnPreferenceChangeListener(mPreferenceChangeListener);
502        } else {
503            PreferenceCategory notificationsCategory = (PreferenceCategory)
504                    findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS);
505            notificationsCategory.removePreference(mAccountVibrateWhen);
506        }
507
508        findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener(
509                new Preference.OnPreferenceClickListener() {
510                    public boolean onPreferenceClick(Preference preference) {
511                        mAccountDirty = true;
512                        mCallback.onIncomingSettings(mAccount);
513                        return true;
514                    }
515                });
516
517        // Hide the outgoing account setup link if it's not activated
518        Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING);
519        boolean showOutgoing = true;
520        try {
521            Sender sender = Sender.getInstance(mContext, mAccount);
522            if (sender != null) {
523                Class<? extends android.app.Activity> setting = sender.getSettingActivityClass();
524                showOutgoing = (setting != null);
525            }
526        } catch (MessagingException me) {
527            // just leave showOutgoing as true - bias towards showing it, so user can fix it
528        }
529        if (showOutgoing) {
530            prefOutgoing.setOnPreferenceClickListener(
531                    new Preference.OnPreferenceClickListener() {
532                        public boolean onPreferenceClick(Preference preference) {
533                            mAccountDirty = true;
534                            mCallback.onOutgoingSettings(mAccount);
535                            return true;
536                        }
537                    });
538        } else {
539            PreferenceCategory serverCategory = (PreferenceCategory) findPreference(
540                    PREFERENCE_CATEGORY_SERVER);
541            serverCategory.removePreference(prefOutgoing);
542        }
543
544        mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS);
545        mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR);
546        mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL);
547        if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
548            android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
549                    AccountManagerTypes.TYPE_EXCHANGE);
550            mSyncContacts.setChecked(ContentResolver
551                    .getSyncAutomatically(acct, ContactsContract.AUTHORITY));
552            mSyncContacts.setOnPreferenceChangeListener(mPreferenceChangeListener);
553            mSyncCalendar.setChecked(ContentResolver
554                    .getSyncAutomatically(acct, CalendarProviderStub.AUTHORITY));
555            mSyncCalendar.setOnPreferenceChangeListener(mPreferenceChangeListener);
556            mSyncEmail.setChecked(ContentResolver
557                    .getSyncAutomatically(acct, EmailContent.AUTHORITY));
558            mSyncEmail.setOnPreferenceChangeListener(mPreferenceChangeListener);
559        } else {
560            PreferenceCategory serverCategory = (PreferenceCategory) findPreference(
561                    PREFERENCE_CATEGORY_SERVER);
562            serverCategory.removePreference(mSyncContacts);
563            serverCategory.removePreference(mSyncCalendar);
564            serverCategory.removePreference(mSyncEmail);
565        }
566
567        // Temporary home for delete account
568        Preference prefDeleteAccount = findPreference(PREFERENCE_DELETE_ACCOUNT);
569        prefDeleteAccount.setOnPreferenceClickListener(
570                new Preference.OnPreferenceClickListener() {
571                    public boolean onPreferenceClick(Preference preference) {
572                        DeleteAccountFragment dialogFragment = DeleteAccountFragment.newInstance(
573                                mAccount, AccountSettingsFragment.this);
574                        FragmentTransaction ft = getFragmentManager().beginTransaction();
575                        ft.addToBackStack(null);
576                        dialogFragment.show(ft, DeleteAccountFragment.TAG);
577                        return true;
578                    }
579                });
580    }
581
582    /**
583     * Generic onPreferenceChanged listener for the preferences (above) that just need
584     * to be written, without extra tweaks
585     */
586    private final Preference.OnPreferenceChangeListener mPreferenceChangeListener =
587        new Preference.OnPreferenceChangeListener() {
588            public boolean onPreferenceChange(Preference preference, Object newValue) {
589                onPreferenceChanged(preference.getKey(), newValue);
590                return true;
591            }
592    };
593
594    /**
595     * Called any time a preference is changed.
596     */
597    private void onPreferenceChanged(String preference, Object value) {
598        mCallback.onSettingsChanged(mAccount, preference, value);
599        mSaveOnExit = true;
600    }
601
602    /*
603     * Note: This writes the settings on the UI thread.  This has to be done so the settings are
604     * committed before we might be killed.
605     */
606    private void saveSettings() {
607        // Turn off all controlled flags - will turn them back on while checking UI elements
608        int newFlags = mAccount.getFlags() &
609                ~(Account.FLAGS_NOTIFY_NEW_MAIL |
610                        Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_VIBRATE_WHEN_SILENT |
611                        Account.FLAGS_BACKGROUND_ATTACHMENTS);
612
613        newFlags |= mAccountBackgroundAttachments.isChecked() ?
614                Account.FLAGS_BACKGROUND_ATTACHMENTS : 0;
615        mAccount.setDefaultAccount(mAccountDefault.isChecked());
616        // If the display name has been cleared, we'll reset it to the default value (email addr)
617        mAccount.setDisplayName(mAccountDescription.getText().trim());
618        // The sender name must never be empty (this is enforced by the preference editor)
619        mAccount.setSenderName(mAccountName.getText().trim());
620        mAccount.setSignature(mAccountSignature.getText());
621        newFlags |= mAccountNotify.isChecked() ? Account.FLAGS_NOTIFY_NEW_MAIL : 0;
622        mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue()));
623        if (mSyncWindow != null) {
624            mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue()));
625        }
626        if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS)) {
627            newFlags |= Account.FLAGS_VIBRATE_ALWAYS;
628        } else if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_SILENT)) {
629            newFlags |= Account.FLAGS_VIBRATE_WHEN_SILENT;
630        }
631        SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
632        mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));
633        mAccount.setFlags(newFlags);
634
635        if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
636            android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
637                    AccountManagerTypes.TYPE_EXCHANGE);
638            ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY,
639                    mSyncContacts.isChecked());
640            ContentResolver.setSyncAutomatically(acct, CalendarProviderStub.AUTHORITY,
641                    mSyncCalendar.isChecked());
642            ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY,
643                    mSyncEmail.isChecked());
644        }
645
646        // Commit the changes
647        // Note, this is done in the UI thread because at this point, we must commit
648        // all changes - any time after onPause completes, we could be killed.  This is analogous
649        // to the way that SharedPreferences tries to work off-thread in apply(), but will pause
650        // until completion in onPause().
651        ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount);
652        mAccount.update(mContext, cv);
653
654        // Run the remaining changes off-thread
655        Email.setServicesEnabledAsync(mContext);
656    }
657
658    /**
659     * Dialog fragment to show "remove account?" dialog
660     */
661    public static class DeleteAccountFragment extends DialogFragment {
662        private final static String TAG = "DeleteAccountFragment";
663
664        // Argument bundle keys
665        private final static String BUNDLE_KEY_ACCOUNT_NAME = "DeleteAccountFragment.Name";
666
667        /**
668         * Create the dialog with parameters
669         */
670        public static DeleteAccountFragment newInstance(Account account, Fragment parentFragment) {
671            DeleteAccountFragment f = new DeleteAccountFragment();
672            Bundle b = new Bundle();
673            b.putString(BUNDLE_KEY_ACCOUNT_NAME, account.getDisplayName());
674            f.setArguments(b);
675            f.setTargetFragment(parentFragment, 0);
676            return f;
677        }
678
679        @Override
680        public Dialog onCreateDialog(Bundle savedInstanceState) {
681            Context context = getActivity();
682            final String name = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME);
683
684            return new AlertDialog.Builder(context)
685                .setIconAttribute(android.R.attr.alertDialogIcon)
686                .setTitle(R.string.account_delete_dlg_title)
687                .setMessage(context.getString(R.string.account_delete_dlg_instructions_fmt, name))
688                .setPositiveButton(
689                        R.string.okay_action,
690                        new DialogInterface.OnClickListener() {
691                            public void onClick(DialogInterface dialog, int whichButton) {
692                                Fragment f = getTargetFragment();
693                                if (f instanceof AccountSettingsFragment) {
694                                    ((AccountSettingsFragment)f).finishDeleteAccount();
695                                }
696                                dismiss();
697                            }
698                        })
699                .setNegativeButton(
700                        R.string.cancel_action,
701                        new DialogInterface.OnClickListener() {
702                            public void onClick(DialogInterface dialog, int whichButton) {
703                                dismiss();
704                            }
705                        })
706                .create();
707        }
708    }
709
710    /**
711     * Callback from delete account dialog - passes the delete command up to the activity
712     */
713    private void finishDeleteAccount() {
714        mSaveOnExit = false;
715        mCallback.deleteAccount(mAccount);
716    }
717
718    public String getAccountEmail() {
719        // Get the e-mail address of the account being editted, if this is for an existing account.
720        return mAccountEmail;
721    }
722}
723