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