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 = "account_settings_vibrate";
     82     private static final String PREFERENCE_VIBRATE_OLD = "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     private EditTextPreference mAccountDescription;
     93     private EditTextPreference mAccountName;
     94     private EditTextPreference mAccountSignature;
     95     private ListPreference mCheckFrequency;
     96     private ListPreference mSyncWindow;
     97     private CheckBoxPreference mAccountBackgroundAttachments;
     98     private CheckBoxPreference mAccountDefault;
     99     private CheckBoxPreference mAccountNotify;
    100     private CheckBoxPreference mAccountVibrate;
    101     private RingtonePreference mAccountRingtone;
    102     private CheckBoxPreference mSyncContacts;
    103     private CheckBoxPreference mSyncCalendar;
    104     private CheckBoxPreference mSyncEmail;
    105 
    106     private Context mContext;
    107     private Account mAccount;
    108     private boolean mAccountDirty;
    109     private long mDefaultAccountId;
    110     private Callback mCallback = EmptyCallback.INSTANCE;
    111     private boolean mStarted;
    112     private boolean mLoaded;
    113     private boolean mSaveOnExit;
    114 
    115     /** The e-mail of the account being edited. */
    116     private String mAccountEmail;
    117 
    118     // Async Tasks
    119     private AsyncTask<?,?,?> mLoadAccountTask;
    120 
    121     /**
    122      * Callback interface that owning activities must provide
    123      */
    124     public interface Callback {
    125         public void onSettingsChanged(Account account, String preference, Object value);
    126         public void onEditQuickResponses(Account account);
    127         public void onIncomingSettings(Account account);
    128         public void onOutgoingSettings(Account account);
    129         public void abandonEdit();
    130         public void deleteAccount(Account account);
    131     }
    132 
    133     private static class EmptyCallback implements Callback {
    134         public static final Callback INSTANCE = new EmptyCallback();
    135         @Override public void onSettingsChanged(Account account, String preference, Object value) {}
    136         @Override public void onEditQuickResponses(Account account) {}
    137         @Override public void onIncomingSettings(Account account) {}
    138         @Override public void onOutgoingSettings(Account account) {}
    139         @Override public void abandonEdit() {}
    140         @Override public void deleteAccount(Account account) {}
    141     }
    142 
    143     /**
    144      * If launching with an arguments bundle, use this method to build the arguments.
    145      */
    146     public static Bundle buildArguments(long accountId, String email) {
    147         Bundle b = new Bundle();
    148         b.putLong(BUNDLE_KEY_ACCOUNT_ID, accountId);
    149         b.putString(BUNDLE_KEY_ACCOUNT_EMAIL, email);
    150         return b;
    151     }
    152 
    153     public static String getTitleFromArgs(Bundle args) {
    154         return (args == null) ? null : args.getString(BUNDLE_KEY_ACCOUNT_EMAIL);
    155     }
    156 
    157     @Override
    158     public void onAttach(Activity activity) {
    159         super.onAttach(activity);
    160         mContext = activity;
    161     }
    162 
    163     /**
    164      * Called to do initial creation of a fragment.  This is called after
    165      * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
    166      */
    167     @Override
    168     public void onCreate(Bundle savedInstanceState) {
    169         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    170             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onCreate");
    171         }
    172         super.onCreate(savedInstanceState);
    173 
    174         upgradeVibrateSetting();
    175 
    176         // Load the preferences from an XML resource
    177         addPreferencesFromResource(R.xml.account_settings_preferences);
    178 
    179         // Start loading the account data, if provided in the arguments
    180         // If not, activity must call startLoadingAccount() directly
    181         Bundle b = getArguments();
    182         if (b != null) {
    183             long accountId = b.getLong(BUNDLE_KEY_ACCOUNT_ID, -1);
    184             mAccountEmail = b.getString(BUNDLE_KEY_ACCOUNT_EMAIL);
    185             if (accountId >= 0 && !mLoaded) {
    186                 startLoadingAccount(accountId);
    187             }
    188         }
    189 
    190         mAccountDirty = false;
    191     }
    192 
    193     /**
    194      * Upgrades the old tri-state vibrate setting to the new boolean value.
    195      */
    196     private void upgradeVibrateSetting() {
    197         final SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences();
    198 
    199         if (!sharedPreferences.contains(PREFERENCE_VIBRATE)) {
    200             // Try to migrate the old one
    201             final boolean vibrate =
    202                     "always".equals(sharedPreferences.getString(PREFERENCE_VIBRATE_OLD, ""));
    203             sharedPreferences.edit().putBoolean(PREFERENCE_VIBRATE, vibrate);
    204         }
    205     }
    206 
    207     @Override
    208     public void onActivityCreated(Bundle savedInstanceState) {
    209         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    210             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onActivityCreated");
    211         }
    212         super.onActivityCreated(savedInstanceState);
    213     }
    214 
    215     /**
    216      * Called when the Fragment is visible to the user.
    217      */
    218     @Override
    219     public void onStart() {
    220         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    221             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStart");
    222         }
    223         super.onStart();
    224         mStarted = true;
    225 
    226         // If the loaded account is ready now, load the UI
    227         if (mAccount != null && !mLoaded) {
    228             loadSettings();
    229         }
    230     }
    231 
    232     /**
    233      * Called when the fragment is visible to the user and actively running.
    234      * TODO: Don't read account data on UI thread.  This should be fixed by removing the need
    235      * to do this, not by spinning up yet another thread.
    236      */
    237     @Override
    238     public void onResume() {
    239         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    240             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onResume");
    241         }
    242         super.onResume();
    243 
    244         if (mAccountDirty) {
    245             // if we are coming back from editing incoming or outgoing settings,
    246             // we need to refresh them here so we don't accidentally overwrite the
    247             // old values we're still holding here
    248             mAccount.mHostAuthRecv =
    249                 HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
    250             mAccount.mHostAuthSend =
    251                 HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeySend);
    252             // Because "delete policy" UI is on edit incoming settings, we have
    253             // to refresh that as well.
    254             Account refreshedAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
    255             if (refreshedAccount == null || mAccount.mHostAuthRecv == null
    256                     || mAccount.mHostAuthSend == null) {
    257                 mSaveOnExit = false;
    258                 mCallback.abandonEdit();
    259                 return;
    260             }
    261             mAccount.setDeletePolicy(refreshedAccount.getDeletePolicy());
    262             mAccountDirty = false;
    263         }
    264     }
    265 
    266     @Override
    267     public void onPause() {
    268         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    269             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onPause");
    270         }
    271         super.onPause();
    272         if (mSaveOnExit) {
    273             saveSettings();
    274         }
    275     }
    276 
    277     /**
    278      * Called when the Fragment is no longer started.
    279      */
    280     @Override
    281     public void onStop() {
    282         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    283             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStop");
    284         }
    285         super.onStop();
    286         mStarted = false;
    287     }
    288 
    289     /**
    290      * Called when the fragment is no longer in use.
    291      */
    292     @Override
    293     public void onDestroy() {
    294         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    295             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy");
    296         }
    297         super.onDestroy();
    298 
    299         Utility.cancelTaskInterrupt(mLoadAccountTask);
    300         mLoadAccountTask = null;
    301     }
    302 
    303     @Override
    304     public void onSaveInstanceState(Bundle outState) {
    305         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
    306             Log.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState");
    307         }
    308         super.onSaveInstanceState(outState);
    309     }
    310 
    311     /**
    312      * Activity provides callbacks here
    313      */
    314     public void setCallback(Callback callback) {
    315         mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
    316     }
    317 
    318     /**
    319      * Start loading a single account in preparation for editing it
    320      */
    321     public void startLoadingAccount(long accountId) {
    322         Utility.cancelTaskInterrupt(mLoadAccountTask);
    323         mLoadAccountTask = new LoadAccountTask().executeOnExecutor(
    324                 AsyncTask.THREAD_POOL_EXECUTOR, accountId);
    325     }
    326 
    327     /**
    328      * Async task to load account in order to view/edit it
    329      */
    330     private class LoadAccountTask extends AsyncTask<Long, Void, Object[]> {
    331         @Override
    332         protected Object[] doInBackground(Long... params) {
    333             long accountId = params[0];
    334             Account account = Account.restoreAccountWithId(mContext, accountId);
    335             if (account != null) {
    336                 account.mHostAuthRecv =
    337                     HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv);
    338                 account.mHostAuthSend =
    339                     HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend);
    340                 if (account.mHostAuthRecv == null || account.mHostAuthSend == null) {
    341                     account = null;
    342                 }
    343             }
    344             long defaultAccountId = Account.getDefaultAccountId(mContext);
    345             return new Object[] { account, Long.valueOf(defaultAccountId) };
    346         }
    347 
    348         @Override
    349         protected void onPostExecute(Object[] results) {
    350             if (results != null && !isCancelled()) {
    351                 Account account = (Account) results[0];
    352                 if (account == null) {
    353                     mSaveOnExit = false;
    354                     mCallback.abandonEdit();
    355                 } else {
    356                     mAccount = account;
    357                     mDefaultAccountId = (Long) results[1];
    358                     if (mStarted && !mLoaded) {
    359                         loadSettings();
    360                     }
    361                 }
    362             }
    363         }
    364     }
    365 
    366     /**
    367      * Load account data into preference UI
    368      */
    369     private void loadSettings() {
    370         // We can only do this once, so prevent repeat
    371         mLoaded = true;
    372         // Once loaded the data is ready to be saved, as well
    373         mSaveOnExit = false;
    374 
    375         mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION);
    376         mAccountDescription.setSummary(mAccount.getDisplayName());
    377         mAccountDescription.setText(mAccount.getDisplayName());
    378         mAccountDescription.setOnPreferenceChangeListener(
    379             new Preference.OnPreferenceChangeListener() {
    380                 public boolean onPreferenceChange(Preference preference, Object newValue) {
    381                     String summary = newValue.toString().trim();
    382                     if (TextUtils.isEmpty(summary)) {
    383                         summary = mAccount.mEmailAddress;
    384                     }
    385                     mAccountDescription.setSummary(summary);
    386                     mAccountDescription.setText(summary);
    387                     onPreferenceChanged(PREFERENCE_DESCRIPTION, summary);
    388                     return false;
    389                 }
    390             }
    391         );
    392 
    393         mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME);
    394         String senderName = mAccount.getSenderName();
    395         // In rare cases, sendername will be null;  Change this to empty string to avoid NPE's
    396         if (senderName == null) senderName = "";
    397         mAccountName.setSummary(senderName);
    398         mAccountName.setText(senderName);
    399         mAccountName.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
    400             public boolean onPreferenceChange(Preference preference, Object newValue) {
    401                 final String summary = newValue.toString().trim();
    402                 if (!TextUtils.isEmpty(summary)) {
    403                     mAccountName.setSummary(summary);
    404                     mAccountName.setText(summary);
    405                     onPreferenceChanged(PREFERENCE_NAME, summary);
    406                 }
    407                 return false;
    408             }
    409         });
    410 
    411         mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE);
    412         String signature = mAccount.getSignature();
    413         mAccountSignature.setText(mAccount.getSignature());
    414         mAccountSignature.setOnPreferenceChangeListener(
    415             new Preference.OnPreferenceChangeListener() {
    416                 public boolean onPreferenceChange(Preference preference, Object newValue) {
    417                     // Clean up signature if it's only whitespace (which is easy to do on a
    418                     // soft keyboard) but leave whitespace in place otherwise, to give the user
    419                     // maximum flexibility, e.g. the ability to indent
    420                     String signature = newValue.toString();
    421                     if (signature.trim().isEmpty()) {
    422                         signature = "";
    423                     }
    424                     mAccountSignature.setText(signature);
    425                     onPreferenceChanged(PREFERENCE_SIGNATURE, signature);
    426                     return false;
    427                 }
    428             });
    429 
    430         mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
    431 
    432         // TODO Move protocol into Account to avoid retrieving the HostAuth (implicitly)
    433         String protocol = Account.getProtocol(mContext, mAccount.mId);
    434         if (HostAuth.SCHEME_EAS.equals(protocol)) {
    435             mCheckFrequency.setEntries(R.array.account_settings_check_frequency_entries_push);
    436             mCheckFrequency.setEntryValues(R.array.account_settings_check_frequency_values_push);
    437         }
    438 
    439         mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval()));
    440         mCheckFrequency.setSummary(mCheckFrequency.getEntry());
    441         mCheckFrequency.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
    442             public boolean onPreferenceChange(Preference preference, Object newValue) {
    443                 final String summary = newValue.toString();
    444                 int index = mCheckFrequency.findIndexOfValue(summary);
    445                 mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]);
    446                 mCheckFrequency.setValue(summary);
    447                 onPreferenceChanged(PREFERENCE_FREQUENCY, newValue);
    448                 return false;
    449             }
    450         });
    451 
    452         findPreference(PREFERENCE_QUICK_RESPONSES).setOnPreferenceClickListener(
    453                 new Preference.OnPreferenceClickListener() {
    454                     @Override
    455                     public boolean onPreferenceClick(Preference preference) {
    456                         mAccountDirty = true;
    457                         mCallback.onEditQuickResponses(mAccount);
    458                         return true;
    459                     }
    460                 });
    461 
    462         // Add check window preference
    463         PreferenceCategory dataUsageCategory =
    464                 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE);
    465 
    466         mSyncWindow = null;
    467         if (HostAuth.SCHEME_EAS.equals(protocol)) {
    468             mSyncWindow = new ListPreference(mContext);
    469             mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label);
    470             mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback()));
    471             mSyncWindow.setSummary(mSyncWindow.getEntry());
    472             MailboxSettings.setupLookbackPreferenceOptions(mContext, mSyncWindow, mAccount);
    473 
    474             // Must correspond to the hole in the XML file that's reserved.
    475             mSyncWindow.setOrder(2);
    476             mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
    477                 public boolean onPreferenceChange(Preference preference, Object newValue) {
    478                     final String summary = newValue.toString();
    479                     int index = mSyncWindow.findIndexOfValue(summary);
    480                     mSyncWindow.setSummary(mSyncWindow.getEntries()[index]);
    481                     mSyncWindow.setValue(summary);
    482                     onPreferenceChanged(preference.getKey(), newValue);
    483                     return false;
    484                 }
    485             });
    486             dataUsageCategory.addPreference(mSyncWindow);
    487         }
    488 
    489         // Show "background attachments" for IMAP & EAS - hide it for POP3.
    490         mAccountBackgroundAttachments = (CheckBoxPreference)
    491                 findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS);
    492         if (HostAuth.SCHEME_POP3.equals(mAccount.mHostAuthRecv.mProtocol)) {
    493             dataUsageCategory.removePreference(mAccountBackgroundAttachments);
    494         } else {
    495             mAccountBackgroundAttachments.setChecked(
    496                     0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS));
    497             mAccountBackgroundAttachments.setOnPreferenceChangeListener(mPreferenceChangeListener);
    498         }
    499 
    500         mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT);
    501         mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId);
    502         mAccountDefault.setOnPreferenceChangeListener(mPreferenceChangeListener);
    503 
    504         mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY);
    505         mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL));
    506         mAccountNotify.setOnPreferenceChangeListener(mPreferenceChangeListener);
    507 
    508         mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE);
    509         mAccountRingtone.setOnPreferenceChangeListener(mPreferenceChangeListener);
    510 
    511         // The following two lines act as a workaround for the RingtonePreference
    512         // which does not let us set/get the value programmatically
    513         SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
    514         prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply();
    515 
    516         // Set the vibrator value, or hide it on devices w/o a vibrator
    517         mAccountVibrate = (CheckBoxPreference) findPreference(PREFERENCE_VIBRATE);
    518         Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
    519         if (vibrator.hasVibrator()) {
    520             // Calculate the value to set based on the choices, and set the value.
    521             final boolean vibrate = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE);
    522             mAccountVibrate.setChecked(vibrate);
    523 
    524             // When the value is changed, update the setting.
    525             mAccountVibrate.setOnPreferenceChangeListener(
    526                     new Preference.OnPreferenceChangeListener() {
    527                         @Override
    528                         public boolean onPreferenceChange(Preference preference, Object newValue) {
    529                             final boolean vibrateSetting = (Boolean) newValue;
    530                             mAccountVibrate.setChecked(vibrateSetting);
    531                             onPreferenceChanged(PREFERENCE_VIBRATE, 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(mAccountVibrate);
    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 |
    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 (mAccountVibrate.isChecked()) {
    659             newFlags |= Account.FLAGS_VIBRATE;
    660         }
    661         SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences();
    662         mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null));
    663         mAccount.setFlags(newFlags);
    664 
    665         if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
    666             android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
    667                     AccountManagerTypes.TYPE_EXCHANGE);
    668             ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY,
    669                     mSyncContacts.isChecked());
    670             ContentResolver.setSyncAutomatically(acct, CalendarProviderStub.AUTHORITY,
    671                     mSyncCalendar.isChecked());
    672             ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY,
    673                     mSyncEmail.isChecked());
    674         }
    675 
    676         // Commit the changes
    677         // Note, this is done in the UI thread because at this point, we must commit
    678         // all changes - any time after onPause completes, we could be killed.  This is analogous
    679         // to the way that SharedPreferences tries to work off-thread in apply(), but will pause
    680         // until completion in onPause().
    681         ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount);
    682         mAccount.update(mContext, cv);
    683 
    684         // Run the remaining changes off-thread
    685         Email.setServicesEnabledAsync(mContext);
    686     }
    687 
    688     /**
    689      * Dialog fragment to show "remove account?" dialog
    690      */
    691     public static class DeleteAccountFragment extends DialogFragment {
    692         private final static String TAG = "DeleteAccountFragment";
    693 
    694         // Argument bundle keys
    695         private final static String BUNDLE_KEY_ACCOUNT_NAME = "DeleteAccountFragment.Name";
    696 
    697         /**
    698          * Create the dialog with parameters
    699          */
    700         public static DeleteAccountFragment newInstance(Account account, Fragment parentFragment) {
    701             DeleteAccountFragment f = new DeleteAccountFragment();
    702             Bundle b = new Bundle();
    703             b.putString(BUNDLE_KEY_ACCOUNT_NAME, account.getDisplayName());
    704             f.setArguments(b);
    705             f.setTargetFragment(parentFragment, 0);
    706             return f;
    707         }
    708 
    709         @Override
    710         public Dialog onCreateDialog(Bundle savedInstanceState) {
    711             Context context = getActivity();
    712             final String name = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME);
    713 
    714             return new AlertDialog.Builder(context)
    715                 .setIconAttribute(android.R.attr.alertDialogIcon)
    716                 .setTitle(R.string.account_delete_dlg_title)
    717                 .setMessage(context.getString(R.string.account_delete_dlg_instructions_fmt, name))
    718                 .setPositiveButton(
    719                         R.string.okay_action,
    720                         new DialogInterface.OnClickListener() {
    721                             public void onClick(DialogInterface dialog, int whichButton) {
    722                                 Fragment f = getTargetFragment();
    723                                 if (f instanceof AccountSettingsFragment) {
    724                                     ((AccountSettingsFragment)f).finishDeleteAccount();
    725                                 }
    726                                 dismiss();
    727                             }
    728                         })
    729                 .setNegativeButton(
    730                         R.string.cancel_action,
    731                         new DialogInterface.OnClickListener() {
    732                             public void onClick(DialogInterface dialog, int whichButton) {
    733                                 dismiss();
    734                             }
    735                         })
    736                 .create();
    737         }
    738     }
    739 
    740     /**
    741      * Callback from delete account dialog - passes the delete command up to the activity
    742      */
    743     private void finishDeleteAccount() {
    744         mSaveOnExit = false;
    745         mCallback.deleteAccount(mAccount);
    746     }
    747 
    748     public String getAccountEmail() {
    749         // Get the e-mail address of the account being editted, if this is for an existing account.
    750         return mAccountEmail;
    751     }
    752 }
    753