AccountSettingsFragment.java revision 12b82d9374947c9268217f45befe8a74bd9b60d7
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 com.android.email.Email;
20import com.android.email.R;
21import com.android.email.mail.Sender;
22import com.android.email.mail.Store;
23import com.android.emailcommon.AccountManagerTypes;
24import com.android.emailcommon.CalendarProviderStub;
25import com.android.emailcommon.Logging;
26import com.android.emailcommon.mail.MessagingException;
27import com.android.emailcommon.provider.EmailContent;
28import com.android.emailcommon.provider.EmailContent.Account;
29import com.android.emailcommon.provider.HostAuth;
30import com.android.emailcommon.utility.Utility;
31
32import android.app.Activity;
33import android.app.AlertDialog;
34import android.app.Dialog;
35import android.app.DialogFragment;
36import android.app.Fragment;
37import android.app.FragmentTransaction;
38import android.content.ContentResolver;
39import android.content.ContentValues;
40import android.content.Context;
41import android.content.DialogInterface;
42import android.content.SharedPreferences;
43import android.os.AsyncTask;
44import android.os.Bundle;
45import android.os.Vibrator;
46import android.preference.CheckBoxPreference;
47import android.preference.EditTextPreference;
48import android.preference.ListPreference;
49import android.preference.Preference;
50import android.preference.PreferenceActivity;
51import android.preference.PreferenceCategory;
52import android.preference.PreferenceFragment;
53import android.preference.RingtonePreference;
54import android.provider.ContactsContract;
55import android.text.TextUtils;
56import android.util.Log;
57
58/**
59 * Fragment containing the main logic for account settings.  This also calls out to other
60 * fragments for server settings.
61 *
62 * TODO: Remove or make async the mAccountDirty reload logic.  Probably no longer needed.
63 * TODO: Can we defer calling addPreferencesFromResource() until after we load the account?  This
64 *       could reduce flicker.
65 */
66public class AccountSettingsFragment extends PreferenceFragment {
67
68    // Keys used for arguments bundle
69    private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId";
70    private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email";
71
72    private static final String PREFERENCE_CATEGORY_TOP = "account_settings";
73    public static final String PREFERENCE_DESCRIPTION = "account_description";
74    private static final String PREFERENCE_NAME = "account_name";
75    private static final String PREFERENCE_SIGNATURE = "account_signature";
76    private static final String PREFERENCE_FREQUENCY = "account_check_frequency";
77    private static final String PREFERENCE_BACKGROUND_ATTACHMENTS =
78            "account_background_attachments";
79    private static final String PREFERENCE_DEFAULT = "account_default";
80    private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications";
81    private static final String PREFERENCE_NOTIFY = "account_notify";
82    private static final String PREFERENCE_VIBRATE_WHEN = "account_settings_vibrate_when";
83    private static final String PREFERENCE_RINGTONE = "account_ringtone";
84    private static final String PREFERENCE_CATEGORY_SERVER = "account_servers";
85    private static final String PREFERENCE_INCOMING = "incoming";
86    private static final String PREFERENCE_OUTGOING = "outgoing";
87    private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts";
88    private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar";
89    private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email";
90    private static final String PREFERENCE_DELETE_ACCOUNT = "delete_account";
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 onIncomingSettings(Account account);
132        public void onOutgoingSettings(Account account);
133        public void abandonEdit();
134        public void deleteAccount(Account account);
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 onIncomingSettings(Account account) {}
141        @Override public void onOutgoingSettings(Account account) {}
142        @Override public void abandonEdit() {}
143        @Override public void deleteAccount(Account account) {}
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 && Email.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 && Email.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 && Email.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 && Email.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 && Email.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 && Email.DEBUG) {
270            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStop");
271        }
272        super.onStop();
273        mStarted = false;
274    }
275
276    /**
277     * Called when the fragment is no longer in use.
278     */
279    @Override
280    public void onDestroy() {
281        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
282            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy");
283        }
284        super.onDestroy();
285
286        Utility.cancelTaskInterrupt(mLoadAccountTask);
287        mLoadAccountTask = null;
288    }
289
290    @Override
291    public void onSaveInstanceState(Bundle outState) {
292        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
293            Log.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState");
294        }
295        super.onSaveInstanceState(outState);
296    }
297
298    /**
299     * Activity provides callbacks here
300     */
301    public void setCallback(Callback callback) {
302        mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
303    }
304
305    /**
306     * Start loading a single account in preparation for editing it
307     */
308    public void startLoadingAccount(long accountId) {
309        Utility.cancelTaskInterrupt(mLoadAccountTask);
310        mLoadAccountTask = new LoadAccountTask().execute(accountId);
311    }
312
313    /**
314     * Async task to load account in order to view/edit it
315     */
316    private class LoadAccountTask extends AsyncTask<Long, Void, Object[]> {
317        @Override
318        protected Object[] doInBackground(Long... params) {
319            long accountId = params[0];
320            Account account = Account.restoreAccountWithId(mContext, accountId);
321            if (account != null) {
322                account.mHostAuthRecv =
323                    HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv);
324                account.mHostAuthSend =
325                    HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend);
326                if (account.mHostAuthRecv == null || account.mHostAuthSend == null) {
327                    account = null;
328                }
329            }
330            long defaultAccountId = Account.getDefaultAccountId(mContext);
331            return new Object[] { account, Long.valueOf(defaultAccountId) };
332        }
333
334        @Override
335        protected void onPostExecute(Object[] results) {
336            if (results != null && !isCancelled()) {
337                Account account = (Account) results[0];
338                if (account == null) {
339                    mSaveOnExit = false;
340                    mCallback.abandonEdit();
341                } else {
342                    mAccount = account;
343                    mDefaultAccountId = (Long) results[1];
344                    if (mStarted && !mLoaded) {
345                        loadSettings();
346                    }
347                }
348            }
349        }
350    }
351
352    /**
353     * Load account data into preference UI
354     */
355    private void loadSettings() {
356        // We can only do this once, so prevent repeat
357        mLoaded = true;
358        // Once loaded the data is ready to be saved, as well
359        mSaveOnExit = false;
360
361        PreferenceCategory topCategory =
362            (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_TOP);
363        topCategory.setTitle(mContext.getString(R.string.account_settings_title_fmt));
364
365        mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION);
366        mAccountDescription.setSummary(mAccount.getDisplayName());
367        mAccountDescription.setText(mAccount.getDisplayName());
368        mAccountDescription.setOnPreferenceChangeListener(
369            new Preference.OnPreferenceChangeListener() {
370                public boolean onPreferenceChange(Preference preference, Object newValue) {
371                    String summary = newValue.toString().trim();
372                    if (TextUtils.isEmpty(summary)) {
373                        summary = mAccount.mEmailAddress;
374                    }
375                    mAccountDescription.setSummary(summary);
376                    mAccountDescription.setText(summary);
377                    onPreferenceChanged(PREFERENCE_DESCRIPTION, summary);
378                    return false;
379                }
380            }
381        );
382
383        mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME);
384        String senderName = mAccount.getSenderName();
385        // In rare cases, sendername will be null;  Change this to empty string to avoid NPE's
386        if (senderName == null) senderName = "";
387        mAccountName.setSummary(senderName);
388        mAccountName.setText(senderName);
389        mAccountName.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
390            public boolean onPreferenceChange(Preference preference, Object newValue) {
391                final String summary = newValue.toString().trim();
392                if (!TextUtils.isEmpty(summary)) {
393                    mAccountName.setSummary(summary);
394                    mAccountName.setText(summary);
395                    onPreferenceChanged(PREFERENCE_NAME, summary);
396                }
397                return false;
398            }
399        });
400
401        mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE);
402        String signature = mAccount.getSignature();
403        mAccountSignature.setText(mAccount.getSignature());
404        mAccountSignature.setOnPreferenceChangeListener(
405            new Preference.OnPreferenceChangeListener() {
406                public boolean onPreferenceChange(Preference preference, Object newValue) {
407                    // Clean up signature if it's only whitespace (which is easy to do on a
408                    // soft keyboard) but leave whitespace in place otherwise, to give the user
409                    // maximum flexibility, e.g. the ability to indent
410                    String signature = newValue.toString();
411                    if (signature.trim().isEmpty()) {
412                        signature = "";
413                    }
414                    mAccountSignature.setText(signature);
415                    onPreferenceChanged(PREFERENCE_SIGNATURE, signature);
416                    return false;
417                }
418            });
419
420        mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
421
422        // Before setting value, we may need to adjust the lists
423        Store.StoreInfo info = Store.StoreInfo.getStoreInfo(mAccount.getStoreUri(mContext),
424                mContext);
425        if (info.mPushSupported) {
426            mCheckFrequency.setEntries(R.array.account_settings_check_frequency_entries_push);
427            mCheckFrequency.setEntryValues(R.array.account_settings_check_frequency_values_push);
428        }
429
430        mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval()));
431        mCheckFrequency.setSummary(mCheckFrequency.getEntry());
432        mCheckFrequency.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
433            public boolean onPreferenceChange(Preference preference, Object newValue) {
434                final String summary = newValue.toString();
435                int index = mCheckFrequency.findIndexOfValue(summary);
436                mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]);
437                mCheckFrequency.setValue(summary);
438                onPreferenceChanged(PREFERENCE_FREQUENCY, newValue);
439                return false;
440            }
441        });
442
443        // Add check window preference
444        mSyncWindow = null;
445        if (info.mVisibleLimitDefault == -1) {
446            mSyncWindow = new ListPreference(mContext);
447            mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label);
448            mSyncWindow.setEntries(R.array.account_settings_mail_window_entries);
449            mSyncWindow.setEntryValues(R.array.account_settings_mail_window_values);
450            mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback()));
451            mSyncWindow.setSummary(mSyncWindow.getEntry());
452            mSyncWindow.setOrder(4);
453            mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
454                public boolean onPreferenceChange(Preference preference, Object newValue) {
455                    final String summary = newValue.toString();
456                    int index = mSyncWindow.findIndexOfValue(summary);
457                    mSyncWindow.setSummary(mSyncWindow.getEntries()[index]);
458                    mSyncWindow.setValue(summary);
459                    onPreferenceChanged(preference.getKey(), newValue);
460                    return false;
461                }
462            });
463            topCategory.addPreference(mSyncWindow);
464        }
465
466        // Show "background attachments" for IMAP & EAS - hide it for POP3.
467        mAccountBackgroundAttachments = (CheckBoxPreference)
468                findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS);
469        if ("pop3".equals(mAccount.mHostAuthRecv.mProtocol)) {
470            topCategory.removePreference(mAccountBackgroundAttachments);
471        } else {
472            mAccountBackgroundAttachments.setChecked(
473                    0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS));
474            mAccountBackgroundAttachments.setOnPreferenceChangeListener(mPreferenceChangeListener);
475        }
476
477        mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT);
478        mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId);
479        mAccountDefault.setOnPreferenceChangeListener(mPreferenceChangeListener);
480
481        mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY);
482        mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL));
483        mAccountNotify.setOnPreferenceChangeListener(mPreferenceChangeListener);
484
485        mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE);
486        mAccountRingtone.setOnPreferenceChangeListener(mPreferenceChangeListener);
487
488        // The following two lines act as a workaround for the RingtonePreference
489        // which does not let us set/get the value programmatically
490        SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
491        prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply();
492
493        // Set the vibrator value, or hide it on devices w/o a vibrator
494        mAccountVibrateWhen = (ListPreference) findPreference(PREFERENCE_VIBRATE_WHEN);
495        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
496        if (vibrator.hasVibrator()) {
497            boolean flagsVibrate = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_ALWAYS);
498            boolean flagsVibrateSilent =
499                    0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_WHEN_SILENT);
500            mAccountVibrateWhen.setValue(
501                    flagsVibrate ? PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS :
502                    flagsVibrateSilent ? PREFERENCE_VALUE_VIBRATE_WHEN_SILENT :
503                        PREFERENCE_VALUE_VIBRATE_WHEN_NEVER);
504            mAccountVibrateWhen.setOnPreferenceChangeListener(mPreferenceChangeListener);
505        } else {
506            PreferenceCategory notificationsCategory = (PreferenceCategory)
507                    findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS);
508            notificationsCategory.removePreference(mAccountVibrateWhen);
509        }
510
511        findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener(
512                new Preference.OnPreferenceClickListener() {
513                    public boolean onPreferenceClick(Preference preference) {
514                        mAccountDirty = true;
515                        mCallback.onIncomingSettings(mAccount);
516                        return true;
517                    }
518                });
519
520        // Hide the outgoing account setup link if it's not activated
521        Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING);
522        boolean showOutgoing = true;
523        try {
524            Sender sender = Sender.getInstance(mContext, mAccount);
525            if (sender != null) {
526                Class<? extends android.app.Activity> setting = sender.getSettingActivityClass();
527                showOutgoing = (setting != null);
528            }
529        } catch (MessagingException me) {
530            // just leave showOutgoing as true - bias towards showing it, so user can fix it
531        }
532        if (showOutgoing) {
533            prefOutgoing.setOnPreferenceClickListener(
534                    new Preference.OnPreferenceClickListener() {
535                        public boolean onPreferenceClick(Preference preference) {
536                            mAccountDirty = true;
537                            mCallback.onOutgoingSettings(mAccount);
538                            return true;
539                        }
540                    });
541        } else {
542            PreferenceCategory serverCategory = (PreferenceCategory) findPreference(
543                    PREFERENCE_CATEGORY_SERVER);
544            serverCategory.removePreference(prefOutgoing);
545        }
546
547        mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS);
548        mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR);
549        mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL);
550        if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
551            android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
552                    AccountManagerTypes.TYPE_EXCHANGE);
553            mSyncContacts.setChecked(ContentResolver
554                    .getSyncAutomatically(acct, ContactsContract.AUTHORITY));
555            mSyncContacts.setOnPreferenceChangeListener(mPreferenceChangeListener);
556            mSyncCalendar.setChecked(ContentResolver
557                    .getSyncAutomatically(acct, CalendarProviderStub.AUTHORITY));
558            mSyncCalendar.setOnPreferenceChangeListener(mPreferenceChangeListener);
559            mSyncEmail.setChecked(ContentResolver
560                    .getSyncAutomatically(acct, EmailContent.AUTHORITY));
561            mSyncEmail.setOnPreferenceChangeListener(mPreferenceChangeListener);
562        } else {
563            PreferenceCategory serverCategory = (PreferenceCategory) findPreference(
564                    PREFERENCE_CATEGORY_SERVER);
565            serverCategory.removePreference(mSyncContacts);
566            serverCategory.removePreference(mSyncCalendar);
567            serverCategory.removePreference(mSyncEmail);
568        }
569
570        // Temporary home for delete account
571        Preference prefDeleteAccount = findPreference(PREFERENCE_DELETE_ACCOUNT);
572        prefDeleteAccount.setOnPreferenceClickListener(
573                new Preference.OnPreferenceClickListener() {
574                    public boolean onPreferenceClick(Preference preference) {
575                        DeleteAccountFragment dialogFragment = DeleteAccountFragment.newInstance(
576                                mAccount, AccountSettingsFragment.this);
577                        FragmentTransaction ft = getFragmentManager().beginTransaction();
578                        ft.addToBackStack(null);
579                        dialogFragment.show(ft, DeleteAccountFragment.TAG);
580                        return true;
581                    }
582                });
583    }
584
585    /**
586     * Generic onPreferenceChanged listener for the preferences (above) that just need
587     * to be written, without extra tweaks
588     */
589    private final Preference.OnPreferenceChangeListener mPreferenceChangeListener =
590        new Preference.OnPreferenceChangeListener() {
591            public boolean onPreferenceChange(Preference preference, Object newValue) {
592                onPreferenceChanged(preference.getKey(), newValue);
593                return true;
594            }
595    };
596
597    /**
598     * Called any time a preference is changed.
599     */
600    private void onPreferenceChanged(String preference, Object value) {
601        mCallback.onSettingsChanged(mAccount, preference, value);
602        mSaveOnExit = true;
603    }
604
605    /*
606     * Note: This writes the settings on the UI thread.  This has to be done so the settings are
607     * committed before we might be killed.
608     */
609    private void saveSettings() {
610        // Turn off all controlled flags - will turn them back on while checking UI elements
611        int newFlags = mAccount.getFlags() &
612                ~(Account.FLAGS_NOTIFY_NEW_MAIL |
613                        Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_VIBRATE_WHEN_SILENT |
614                        Account.FLAGS_BACKGROUND_ATTACHMENTS);
615
616        newFlags |= mAccountBackgroundAttachments.isChecked() ?
617                Account.FLAGS_BACKGROUND_ATTACHMENTS : 0;
618        mAccount.setDefaultAccount(mAccountDefault.isChecked());
619        // If the display name has been cleared, we'll reset it to the default value (email addr)
620        mAccount.setDisplayName(mAccountDescription.getText().trim());
621        // The sender name must never be empty (this is enforced by the preference editor)
622        mAccount.setSenderName(mAccountName.getText().trim());
623        mAccount.setSignature(mAccountSignature.getText());
624        newFlags |= mAccountNotify.isChecked() ? Account.FLAGS_NOTIFY_NEW_MAIL : 0;
625        mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue()));
626        if (mSyncWindow != null) {
627            mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue()));
628        }
629        if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS)) {
630            newFlags |= Account.FLAGS_VIBRATE_ALWAYS;
631        } else if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_SILENT)) {
632            newFlags |= Account.FLAGS_VIBRATE_WHEN_SILENT;
633        }
634        SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
635        mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));
636        mAccount.setFlags(newFlags);
637
638        if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
639            android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
640                    AccountManagerTypes.TYPE_EXCHANGE);
641            ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY,
642                    mSyncContacts.isChecked());
643            ContentResolver.setSyncAutomatically(acct, CalendarProviderStub.AUTHORITY,
644                    mSyncCalendar.isChecked());
645            ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY,
646                    mSyncEmail.isChecked());
647        }
648
649        // Commit the changes
650        // Note, this is done in the UI thread because at this point, we must commit
651        // all changes - any time after onPause completes, we could be killed.  This is analogous
652        // to the way that SharedPreferences tries to work off-thread in apply(), but will pause
653        // until completion in onPause().
654        ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount);
655        mAccount.update(mContext, cv);
656
657        // Run the remaining changes off-thread
658        Email.setServicesEnabledAsync(mContext);
659    }
660
661    /**
662     * Dialog fragment to show "remove account?" dialog
663     */
664    public static class DeleteAccountFragment extends DialogFragment {
665        private final static String TAG = "DeleteAccountFragment";
666
667        // Argument bundle keys
668        private final static String BUNDLE_KEY_ACCOUNT_NAME = "DeleteAccountFragment.Name";
669
670        /**
671         * Create the dialog with parameters
672         */
673        public static DeleteAccountFragment newInstance(Account account, Fragment parentFragment) {
674            DeleteAccountFragment f = new DeleteAccountFragment();
675            Bundle b = new Bundle();
676            b.putString(BUNDLE_KEY_ACCOUNT_NAME, account.getDisplayName());
677            f.setArguments(b);
678            f.setTargetFragment(parentFragment, 0);
679            return f;
680        }
681
682        @Override
683        public Dialog onCreateDialog(Bundle savedInstanceState) {
684            Context context = getActivity();
685            final String name = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME);
686
687            return new AlertDialog.Builder(context)
688                .setIconAttribute(android.R.attr.alertDialogIcon)
689                .setTitle(R.string.account_delete_dlg_title)
690                .setMessage(context.getString(R.string.account_delete_dlg_instructions_fmt, name))
691                .setPositiveButton(
692                        R.string.okay_action,
693                        new DialogInterface.OnClickListener() {
694                            public void onClick(DialogInterface dialog, int whichButton) {
695                                Fragment f = getTargetFragment();
696                                if (f instanceof AccountSettingsFragment) {
697                                    ((AccountSettingsFragment)f).finishDeleteAccount();
698                                }
699                                dismiss();
700                            }
701                        })
702                .setNegativeButton(
703                        R.string.cancel_action,
704                        new DialogInterface.OnClickListener() {
705                            public void onClick(DialogInterface dialog, int whichButton) {
706                                dismiss();
707                            }
708                        })
709                .create();
710        }
711    }
712
713    /**
714     * Callback from delete account dialog - passes the delete command up to the activity
715     */
716    private void finishDeleteAccount() {
717        mSaveOnExit = false;
718        mCallback.deleteAccount(mAccount);
719    }
720
721    public String getAccountEmail() {
722        // Get the e-mail address of the account being editted, if this is for an existing account.
723        return mAccountEmail;
724    }
725}
726