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