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.LoaderManager;
     21 import android.content.ContentResolver;
     22 import android.content.ContentValues;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.Loader;
     26 import android.content.res.Resources;
     27 import android.database.Cursor;
     28 import android.media.Ringtone;
     29 import android.media.RingtoneManager;
     30 import android.net.Uri;
     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.PreferenceActivity;
     38 import android.preference.PreferenceCategory;
     39 import android.preference.Preference.OnPreferenceClickListener;
     40 import android.preference.PreferenceScreen;
     41 import android.provider.CalendarContract;
     42 import android.provider.ContactsContract;
     43 import android.provider.Settings;
     44 import android.support.annotation.NonNull;
     45 import android.text.TextUtils;
     46 import android.view.Menu;
     47 import android.view.MenuInflater;
     48 
     49 import com.android.email.R;
     50 import com.android.email.SecurityPolicy;
     51 import com.android.email.provider.EmailProvider;
     52 import com.android.email.provider.FolderPickerActivity;
     53 import com.android.email.service.EmailServiceUtils;
     54 import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
     55 import com.android.email2.ui.MailActivityEmail;
     56 import com.android.emailcommon.provider.Account;
     57 import com.android.emailcommon.provider.EmailContent;
     58 import com.android.emailcommon.provider.EmailContent.AccountColumns;
     59 import com.android.emailcommon.provider.Mailbox;
     60 import com.android.emailcommon.provider.Policy;
     61 import com.android.mail.preferences.AccountPreferences;
     62 import com.android.mail.preferences.FolderPreferences;
     63 import com.android.mail.providers.Folder;
     64 import com.android.mail.providers.UIProvider;
     65 import com.android.mail.ui.MailAsyncTaskLoader;
     66 import com.android.mail.ui.settings.MailAccountPrefsFragment;
     67 import com.android.mail.ui.settings.SettingsUtils;
     68 import com.android.mail.utils.ContentProviderTask.UpdateTask;
     69 import com.android.mail.utils.LogUtils;
     70 import com.android.mail.utils.NotificationUtils;
     71 
     72 import java.util.ArrayList;
     73 import java.util.HashMap;
     74 import java.util.Map;
     75 
     76 /**
     77  * Fragment containing the main logic for account settings.  This also calls out to other
     78  * fragments for server settings.
     79  *
     80  * TODO: Can we defer calling addPreferencesFromResource() until after we load the account?  This
     81  *       could reduce flicker.
     82  */
     83 public class AccountSettingsFragment extends MailAccountPrefsFragment
     84         implements Preference.OnPreferenceChangeListener {
     85 
     86     private static final String ARG_ACCOUNT_ID = "account_id";
     87 
     88     public static final String PREFERENCE_DESCRIPTION = "account_description";
     89     private static final String PREFERENCE_NAME = "account_name";
     90     private static final String PREFERENCE_SIGNATURE = "account_signature";
     91     private static final String PREFERENCE_QUICK_RESPONSES = "account_quick_responses";
     92     private static final String PREFERENCE_FREQUENCY = "account_check_frequency";
     93     private static final String PREFERENCE_SYNC_WINDOW = "account_sync_window";
     94     private static final String PREFERENCE_SYNC_SETTINGS = "account_sync_settings";
     95     private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email";
     96     private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts";
     97     private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar";
     98     private static final String PREFERENCE_BACKGROUND_ATTACHMENTS =
     99             "account_background_attachments";
    100     private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage";
    101     private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications";
    102     private static final String PREFERENCE_CATEGORY_SERVER = "account_servers";
    103     private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies";
    104     @SuppressWarnings("unused") // temporarily unused pending policy UI
    105     private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced";
    106     @SuppressWarnings("unused") // temporarily unused pending policy UI
    107     private static final String PREFERENCE_POLICIES_UNSUPPORTED = "policies_unsupported";
    108     private static final String PREFERENCE_POLICIES_RETRY_ACCOUNT = "policies_retry_account";
    109     private static final String PREFERENCE_INCOMING = "incoming";
    110     private static final String PREFERENCE_OUTGOING = "outgoing";
    111 
    112     private static final String PREFERENCE_SYSTEM_FOLDERS = "system_folders";
    113     private static final String PREFERENCE_SYSTEM_FOLDERS_TRASH = "system_folders_trash";
    114     private static final String PREFERENCE_SYSTEM_FOLDERS_SENT = "system_folders_sent";
    115 
    116     private static final String SAVESTATE_SYNC_INTERVALS = "savestate_sync_intervals";
    117     private static final String SAVESTATE_SYNC_INTERVAL_STRINGS = "savestate_sync_interval_strings";
    118 
    119     // Request code to start different activities.
    120     private static final int RINGTONE_REQUEST_CODE = 0;
    121 
    122     private EditTextPreference mAccountDescription;
    123     private EditTextPreference mAccountName;
    124     private EditTextPreference mAccountSignature;
    125     private ListPreference mCheckFrequency;
    126     private ListPreference mSyncWindow;
    127     private Preference mSyncSettings;
    128     private CheckBoxPreference mInboxVibrate;
    129     private Preference mInboxRingtone;
    130 
    131     private Context mContext;
    132 
    133     private Account mAccount;
    134     private com.android.mail.providers.Account mUiAccount;
    135     private EmailServiceInfo mServiceInfo;
    136     private Folder mInboxFolder;
    137 
    138     private Ringtone mRingtone;
    139 
    140     /**
    141      * This may be null if the account exists but the inbox has not yet been created in the database
    142      * (waiting for initial sync)
    143      */
    144     private FolderPreferences mInboxFolderPreferences;
    145 
    146     // The email of the account being edited
    147     private String mAccountEmail;
    148 
    149     /**
    150      * If launching with an email address, use this method to build the arguments.
    151      */
    152     public static Bundle buildArguments(final String email) {
    153         final Bundle b = new Bundle(1);
    154         b.putString(ARG_ACCOUNT_EMAIL, email);
    155         return b;
    156     }
    157 
    158     /**
    159      * If launching with an account ID, use this method to build the arguments.
    160      */
    161     public static Bundle buildArguments(final long accountId) {
    162         final Bundle b = new Bundle(1);
    163         b.putLong(ARG_ACCOUNT_ID, accountId);
    164         return b;
    165     }
    166 
    167     @Override
    168     public void onAttach(Activity activity) {
    169         super.onAttach(activity);
    170         mContext = activity;
    171     }
    172 
    173     /**
    174      * Called to do initial creation of a fragment.  This is called after
    175      * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
    176      */
    177     @Override
    178     public void onCreate(Bundle savedInstanceState) {
    179         super.onCreate(savedInstanceState);
    180 
    181         setHasOptionsMenu(true);
    182 
    183         // Load the preferences from an XML resource
    184         addPreferencesFromResource(R.xml.account_settings_preferences);
    185 
    186         if (!getResources().getBoolean(R.bool.quickresponse_supported)) {
    187             final Preference quickResponsePref = findPreference(PREFERENCE_QUICK_RESPONSES);
    188             if (quickResponsePref != null) {
    189                 getPreferenceScreen().removePreference(quickResponsePref);
    190             }
    191         }
    192 
    193         // Start loading the account data, if provided in the arguments
    194         // If not, activity must call startLoadingAccount() directly
    195         Bundle b = getArguments();
    196         if (b != null) {
    197             mAccountEmail = b.getString(ARG_ACCOUNT_EMAIL);
    198         }
    199         if (savedInstanceState != null) {
    200             // We won't know what the correct set of sync interval values and strings are until
    201             // our loader completes. The problem is, that if the sync frequency chooser is
    202             // displayed when the screen rotates, it reinitializes it to the defaults, and doesn't
    203             // correct it after the loader finishes again. See b/13624066
    204             // To work around this, we'll save the current set of sync interval values and strings,
    205             // in onSavedInstanceState, and restore them here.
    206             final CharSequence [] syncIntervalStrings =
    207                     savedInstanceState.getCharSequenceArray(SAVESTATE_SYNC_INTERVAL_STRINGS);
    208             final CharSequence [] syncIntervals =
    209                     savedInstanceState.getCharSequenceArray(SAVESTATE_SYNC_INTERVALS);
    210             mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
    211             if (mCheckFrequency != null) {
    212                 mCheckFrequency.setEntries(syncIntervalStrings);
    213                 mCheckFrequency.setEntryValues(syncIntervals);
    214             }
    215         }
    216     }
    217 
    218     @Override
    219     public void onSaveInstanceState(@NonNull Bundle outstate) {
    220         super.onSaveInstanceState(outstate);
    221         if (mCheckFrequency != null) {
    222             outstate.putCharSequenceArray(SAVESTATE_SYNC_INTERVAL_STRINGS,
    223                     mCheckFrequency.getEntries());
    224             outstate.putCharSequenceArray(SAVESTATE_SYNC_INTERVALS,
    225                     mCheckFrequency.getEntryValues());
    226         }
    227     }
    228 
    229     @Override
    230     public void onActivityCreated(Bundle savedInstanceState) {
    231         super.onActivityCreated(savedInstanceState);
    232         final Bundle args = new Bundle(1);
    233         if (!TextUtils.isEmpty(mAccountEmail)) {
    234             args.putString(AccountLoaderCallbacks.ARG_ACCOUNT_EMAIL, mAccountEmail);
    235         } else {
    236             args.putLong(AccountLoaderCallbacks.ARG_ACCOUNT_ID,
    237                     getArguments().getLong(ARG_ACCOUNT_ID, -1));
    238         }
    239         getLoaderManager().initLoader(0, args, new AccountLoaderCallbacks(getActivity()));
    240     }
    241 
    242     @Override
    243     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    244         switch (requestCode) {
    245             case RINGTONE_REQUEST_CODE:
    246                 if (resultCode == Activity.RESULT_OK && data != null) {
    247                     Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
    248                     setRingtone(uri);
    249                 }
    250                 break;
    251         }
    252     }
    253 
    254     /**
    255      * Sets the current ringtone.
    256      */
    257     private void setRingtone(Uri ringtone) {
    258         if (ringtone != null) {
    259             mInboxFolderPreferences.setNotificationRingtoneUri(ringtone.toString());
    260             mRingtone = RingtoneManager.getRingtone(getActivity(), ringtone);
    261         } else {
    262             // Null means silent was selected.
    263             mInboxFolderPreferences.setNotificationRingtoneUri("");
    264             mRingtone = null;
    265         }
    266 
    267         setRingtoneSummary();
    268     }
    269 
    270     private void setRingtoneSummary() {
    271         final String summary = mRingtone != null ? mRingtone.getTitle(mContext)
    272                 : mContext.getString(R.string.silent_ringtone);
    273 
    274         mInboxRingtone.setSummary(summary);
    275     }
    276 
    277     @Override
    278     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
    279             @NonNull Preference preference) {
    280         final String key = preference.getKey();
    281         if (key.equals(PREFERENCE_SYNC_SETTINGS)) {
    282             startActivity(MailboxSettings.getIntent(getActivity(), mUiAccount.fullFolderListUri,
    283                     mInboxFolder));
    284             return true;
    285         } else {
    286             return super.onPreferenceTreeClick(preferenceScreen, preference);
    287         }
    288     }
    289 
    290     /**
    291      * Listen to all preference changes in this class.
    292      * @param preference The changed Preference
    293      * @param newValue The new value of the Preference
    294      * @return True to update the state of the Preference with the new value
    295      */
    296     @Override
    297     public boolean onPreferenceChange(Preference preference, Object newValue) {
    298         // Can't use a switch here. Falling back to a giant conditional.
    299         final String key = preference.getKey();
    300         final ContentValues cv = new ContentValues(1);
    301         if (key.equals(PREFERENCE_DESCRIPTION)){
    302             String summary = newValue.toString().trim();
    303             if (TextUtils.isEmpty(summary)) {
    304                 summary = mUiAccount.getEmailAddress();
    305             }
    306             mAccountDescription.setSummary(summary);
    307             mAccountDescription.setText(summary);
    308             cv.put(AccountColumns.DISPLAY_NAME, summary);
    309         } else if (key.equals(PREFERENCE_NAME)) {
    310             final String summary = newValue.toString().trim();
    311             if (!TextUtils.isEmpty(summary)) {
    312                 mAccountName.setSummary(summary);
    313                 mAccountName.setText(summary);
    314                 cv.put(AccountColumns.SENDER_NAME, summary);
    315             }
    316         } else if (key.equals(PREFERENCE_SIGNATURE)) {
    317             // Clean up signature if it's only whitespace (which is easy to do on a
    318             // soft keyboard) but leave whitespace in place otherwise, to give the user
    319             // maximum flexibility, e.g. the ability to indent
    320             String signature = newValue.toString();
    321             if (signature.trim().isEmpty()) {
    322                 signature = "";
    323             }
    324             mAccountSignature.setText(signature);
    325             SettingsUtils.updatePreferenceSummary(mAccountSignature, signature,
    326                     R.string.preferences_signature_summary_not_set);
    327             cv.put(AccountColumns.SIGNATURE, signature);
    328         } else if (key.equals(PREFERENCE_FREQUENCY)) {
    329             final String summary = newValue.toString();
    330             final int index = mCheckFrequency.findIndexOfValue(summary);
    331             mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]);
    332             mCheckFrequency.setValue(summary);
    333             if (mServiceInfo.syncContacts || mServiceInfo.syncCalendar) {
    334                 // This account allows syncing of contacts and/or calendar, so we will always have
    335                 // separate preferences to enable or disable syncing of email, contacts, and
    336                 // calendar.
    337                 // The "sync frequency" preference really just needs to control the frequency value
    338                 // in our database.
    339                 cv.put(AccountColumns.SYNC_INTERVAL, Integer.parseInt(summary));
    340             } else {
    341                 // This account only syncs email (not contacts or calendar), which means that we
    342                 // will hide the preference to turn syncing on and off. In this case, we want the
    343                 // sync frequency preference to also control whether or not syncing is enabled at
    344                 // all. If sync is turned off, we will display "sync never" regardless of what the
    345                 // numeric value we have stored says.
    346                 final android.accounts.Account androidAcct = new android.accounts.Account(
    347                         mAccount.mEmailAddress, mServiceInfo.accountType);
    348                 if (Integer.parseInt(summary) == Account.CHECK_INTERVAL_NEVER) {
    349                     // Disable syncing from the account manager. Leave the current sync frequency
    350                     // in the database.
    351                     ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY,
    352                             false);
    353                 } else {
    354                     // Enable syncing from the account manager.
    355                     ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY,
    356                             true);
    357                     cv.put(AccountColumns.SYNC_INTERVAL, Integer.parseInt(summary));
    358                 }
    359             }
    360         } else if (key.equals(PREFERENCE_SYNC_WINDOW)) {
    361             final String summary = newValue.toString();
    362             int index = mSyncWindow.findIndexOfValue(summary);
    363             mSyncWindow.setSummary(mSyncWindow.getEntries()[index]);
    364             mSyncWindow.setValue(summary);
    365             cv.put(AccountColumns.SYNC_LOOKBACK, Integer.parseInt(summary));
    366         } else if (key.equals(PREFERENCE_SYNC_EMAIL)) {
    367             final android.accounts.Account androidAcct = new android.accounts.Account(
    368                     mAccount.mEmailAddress, mServiceInfo.accountType);
    369             ContentResolver.setSyncAutomatically(androidAcct, EmailContent.AUTHORITY,
    370                     (Boolean) newValue);
    371         } else if (key.equals(PREFERENCE_SYNC_CONTACTS)) {
    372             final android.accounts.Account androidAcct = new android.accounts.Account(
    373                     mAccount.mEmailAddress, mServiceInfo.accountType);
    374             ContentResolver.setSyncAutomatically(androidAcct, ContactsContract.AUTHORITY,
    375                     (Boolean) newValue);
    376         } else if (key.equals(PREFERENCE_SYNC_CALENDAR)) {
    377             final android.accounts.Account androidAcct = new android.accounts.Account(
    378                     mAccount.mEmailAddress, mServiceInfo.accountType);
    379             ContentResolver.setSyncAutomatically(androidAcct, CalendarContract.AUTHORITY,
    380                     (Boolean) newValue);
    381         } else if (key.equals(PREFERENCE_BACKGROUND_ATTACHMENTS)) {
    382             int newFlags = mAccount.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS);
    383 
    384             newFlags |= (Boolean) newValue ?
    385                     Account.FLAGS_BACKGROUND_ATTACHMENTS : 0;
    386 
    387             cv.put(AccountColumns.FLAGS, newFlags);
    388         } else if (FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED.equals(key)) {
    389             mInboxFolderPreferences.setNotificationsEnabled((Boolean) newValue);
    390             return true;
    391         } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE.equals(key)) {
    392             final boolean vibrateSetting = (Boolean) newValue;
    393             mInboxVibrate.setChecked(vibrateSetting);
    394             mInboxFolderPreferences.setNotificationVibrateEnabled(vibrateSetting);
    395             return true;
    396         } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE.equals(key)) {
    397             return true;
    398         } else {
    399             // Default behavior, just indicate that the preferences were written
    400             LogUtils.d(LogUtils.TAG, "Unknown preference key %s", key);
    401             return true;
    402         }
    403         if (cv.size() > 0) {
    404             new UpdateTask().run(mContext.getContentResolver(), mAccount.getUri(), cv, null, null);
    405             MailActivityEmail.setServicesEnabledAsync(mContext);
    406         }
    407         return false;
    408     }
    409 
    410     @Override
    411     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    412         menu.clear();
    413         inflater.inflate(R.menu.settings_fragment_menu, menu);
    414     }
    415 
    416     /**
    417      * Async task loader to load account in order to view/edit it
    418      */
    419     private static class AccountLoader extends MailAsyncTaskLoader<Map<String, Object>> {
    420         public static final String RESULT_KEY_ACCOUNT = "account";
    421         private static final String RESULT_KEY_UIACCOUNT_CURSOR = "uiAccountCursor";
    422         public static final String RESULT_KEY_UIACCOUNT = "uiAccount";
    423         public static final String RESULT_KEY_INBOX = "inbox";
    424 
    425         private final ForceLoadContentObserver mObserver;
    426         private final String mAccountEmail;
    427         private final long mAccountId;
    428 
    429         private AccountLoader(Context context, String accountEmail, long accountId) {
    430             super(context);
    431             mObserver = new ForceLoadContentObserver();
    432             mAccountEmail = accountEmail;
    433             mAccountId = accountId;
    434         }
    435 
    436         @Override
    437         public Map<String, Object> loadInBackground() {
    438             final Map<String, Object> map = new HashMap<>();
    439 
    440             final Account account;
    441             if (!TextUtils.isEmpty(mAccountEmail)) {
    442                 account = Account.restoreAccountWithAddress(getContext(), mAccountEmail, mObserver);
    443             } else {
    444                 account = Account.restoreAccountWithId(getContext(), mAccountId, mObserver);
    445             }
    446             if (account == null) {
    447                 return map;
    448             }
    449 
    450             map.put(RESULT_KEY_ACCOUNT, account);
    451 
    452             // We don't monitor these for changes, but they probably won't change in any meaningful
    453             // way
    454             account.getOrCreateHostAuthRecv(getContext());
    455             account.getOrCreateHostAuthSend(getContext());
    456 
    457             if (account.mHostAuthRecv == null) {
    458                 return map;
    459             }
    460 
    461             account.mPolicy =
    462                     Policy.restorePolicyWithId(getContext(), account.mPolicyKey, mObserver);
    463 
    464             final Cursor uiAccountCursor = getContext().getContentResolver().query(
    465                     EmailProvider.uiUri("uiaccount", account.getId()),
    466                     UIProvider.ACCOUNTS_PROJECTION,
    467                     null, null, null);
    468 
    469             if (uiAccountCursor != null) {
    470                 map.put(RESULT_KEY_UIACCOUNT_CURSOR, uiAccountCursor);
    471                 uiAccountCursor.registerContentObserver(mObserver);
    472             } else {
    473                 return map;
    474             }
    475 
    476             if (!uiAccountCursor.moveToFirst()) {
    477                 return map;
    478             }
    479 
    480             final com.android.mail.providers.Account uiAccount =
    481                     com.android.mail.providers.Account.builder().buildFrom(uiAccountCursor);
    482 
    483             map.put(RESULT_KEY_UIACCOUNT, uiAccount);
    484 
    485             final Cursor folderCursor = getContext().getContentResolver().query(
    486                     uiAccount.settings.defaultInbox, UIProvider.FOLDERS_PROJECTION, null, null,
    487                     null);
    488 
    489             final Folder inbox;
    490             try {
    491                 if (folderCursor != null && folderCursor.moveToFirst()) {
    492                     inbox = new Folder(folderCursor);
    493                 } else {
    494                     return map;
    495                 }
    496             } finally {
    497                 if (folderCursor != null) {
    498                     folderCursor.close();
    499                 }
    500             }
    501 
    502             map.put(RESULT_KEY_INBOX, inbox);
    503             return map;
    504         }
    505 
    506         @Override
    507         protected void onDiscardResult(Map<String, Object> result) {
    508             final Account account = (Account) result.get(RESULT_KEY_ACCOUNT);
    509             if (account != null) {
    510                 if (account.mPolicy != null) {
    511                     account.mPolicy.close(getContext());
    512                 }
    513                 account.close(getContext());
    514             }
    515             final Cursor uiAccountCursor = (Cursor) result.get(RESULT_KEY_UIACCOUNT_CURSOR);
    516             if (uiAccountCursor != null) {
    517                 uiAccountCursor.close();
    518             }
    519         }
    520     }
    521 
    522     private class AccountLoaderCallbacks
    523             implements LoaderManager.LoaderCallbacks<Map<String, Object>> {
    524         public static final String ARG_ACCOUNT_EMAIL = "accountEmail";
    525         public static final String ARG_ACCOUNT_ID = "accountId";
    526         private final Context mContext;
    527 
    528         private AccountLoaderCallbacks(Context context) {
    529             mContext = context;
    530         }
    531 
    532         @Override
    533         public void onLoadFinished(Loader<Map<String, Object>> loader, Map<String, Object> data) {
    534             final Activity activity = getActivity();
    535             if (activity == null) {
    536                 return;
    537             }
    538             if (data == null) {
    539                 activity.finish();
    540                 return;
    541             }
    542 
    543             mUiAccount = (com.android.mail.providers.Account)
    544                     data.get(AccountLoader.RESULT_KEY_UIACCOUNT);
    545             mAccount = (Account) data.get(AccountLoader.RESULT_KEY_ACCOUNT);
    546 
    547             if (mAccount != null && (mAccount.mFlags & Account.FLAGS_SECURITY_HOLD) != 0) {
    548                 final Intent i = AccountSecurity.actionUpdateSecurityIntent(mContext,
    549                         mAccount.getId(), true);
    550                 mContext.startActivity(i);
    551                 activity.finish();
    552                 return;
    553             }
    554 
    555             mInboxFolder = (Folder) data.get(AccountLoader.RESULT_KEY_INBOX);
    556 
    557             if (mUiAccount == null || mAccount == null) {
    558                 activity.finish();
    559                 return;
    560             }
    561 
    562             mServiceInfo =
    563                     EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext));
    564 
    565             if (mInboxFolder == null) {
    566                 mInboxFolderPreferences = null;
    567             } else {
    568                 mInboxFolderPreferences = new FolderPreferences(mContext,
    569                         mUiAccount.getEmailAddress(), mInboxFolder, true);
    570             }
    571             loadSettings();
    572         }
    573 
    574         @Override
    575         public Loader<Map<String, Object>> onCreateLoader(int id, Bundle args) {
    576             return new AccountLoader(mContext, args.getString(ARG_ACCOUNT_EMAIL),
    577                     args.getLong(ARG_ACCOUNT_ID));
    578         }
    579 
    580         @Override
    581         public void onLoaderReset(Loader<Map<String, Object>> loader) {}
    582     }
    583 
    584     /**
    585      * From a Policy, create and return an ArrayList of Strings that describe (simply) those
    586      * policies that are supported by the OS.  At the moment, the strings are simple (e.g.
    587      * "password required"); we should probably add more information (# characters, etc.), though
    588      */
    589     @SuppressWarnings("unused") // temporarily unused pending policy UI
    590     private ArrayList<String> getSystemPoliciesList(Policy policy) {
    591         Resources res = mContext.getResources();
    592         ArrayList<String> policies = new ArrayList<>();
    593         if (policy.mPasswordMode != Policy.PASSWORD_MODE_NONE) {
    594             policies.add(res.getString(R.string.policy_require_password));
    595         }
    596         if (policy.mPasswordHistory > 0) {
    597             policies.add(res.getString(R.string.policy_password_history));
    598         }
    599         if (policy.mPasswordExpirationDays > 0) {
    600             policies.add(res.getString(R.string.policy_password_expiration));
    601         }
    602         if (policy.mMaxScreenLockTime > 0) {
    603             policies.add(res.getString(R.string.policy_screen_timeout));
    604         }
    605         if (policy.mDontAllowCamera) {
    606             policies.add(res.getString(R.string.policy_dont_allow_camera));
    607         }
    608         if (policy.mMaxEmailLookback != 0) {
    609             policies.add(res.getString(R.string.policy_email_age));
    610         }
    611         if (policy.mMaxCalendarLookback != 0) {
    612             policies.add(res.getString(R.string.policy_calendar_age));
    613         }
    614         return policies;
    615     }
    616 
    617     @SuppressWarnings("unused") // temporarily unused pending policy UI
    618     private void setPolicyListSummary(ArrayList<String> policies, String policiesToAdd,
    619             String preferenceName) {
    620         Policy.addPolicyStringToList(policiesToAdd, policies);
    621         if (policies.size() > 0) {
    622             Preference p = findPreference(preferenceName);
    623             StringBuilder sb = new StringBuilder();
    624             for (String desc: policies) {
    625                 sb.append(desc);
    626                 sb.append('\n');
    627             }
    628             p.setSummary(sb.toString());
    629         }
    630     }
    631 
    632     /**
    633      * Load account data into preference UI. This must be called on the main thread.
    634      */
    635     private void loadSettings() {
    636         final AccountPreferences accountPreferences =
    637                 new AccountPreferences(mContext, mUiAccount.getEmailAddress());
    638         if (mInboxFolderPreferences != null) {
    639             NotificationUtils.moveNotificationSetting(
    640                     accountPreferences, mInboxFolderPreferences);
    641         }
    642 
    643         final String protocol = mAccount.getProtocol(mContext);
    644         if (mServiceInfo == null) {
    645             LogUtils.e(LogUtils.TAG,
    646                     "Could not find service info for account %d with protocol %s", mAccount.mId,
    647                     protocol);
    648             getActivity().onBackPressed();
    649             // TODO: put up some sort of dialog/toast here to tell the user something went wrong
    650             return;
    651         }
    652         final android.accounts.Account androidAcct = mUiAccount.getAccountManagerAccount();
    653 
    654         mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION);
    655         mAccountDescription.setSummary(mAccount.getDisplayName());
    656         mAccountDescription.setText(mAccount.getDisplayName());
    657         mAccountDescription.setOnPreferenceChangeListener(this);
    658 
    659         mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME);
    660         String senderName = mUiAccount.getSenderName();
    661         // In rare cases, sendername will be null;  Change this to empty string to avoid NPE's
    662         if (senderName == null) {
    663             senderName = "";
    664         }
    665         mAccountName.setSummary(senderName);
    666         mAccountName.setText(senderName);
    667         mAccountName.setOnPreferenceChangeListener(this);
    668 
    669         final String accountSignature = mAccount.getSignature();
    670         mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE);
    671         mAccountSignature.setText(accountSignature);
    672         mAccountSignature.setOnPreferenceChangeListener(this);
    673         SettingsUtils.updatePreferenceSummary(mAccountSignature, accountSignature,
    674                 R.string.preferences_signature_summary_not_set);
    675 
    676         mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY);
    677         mCheckFrequency.setEntries(mServiceInfo.syncIntervalStrings);
    678         mCheckFrequency.setEntryValues(mServiceInfo.syncIntervals);
    679         if (mServiceInfo.syncContacts || mServiceInfo.syncCalendar) {
    680             // This account allows syncing of contacts and/or calendar, so we will always have
    681             // separate preferences to enable or disable syncing of email, contacts, and calendar.
    682             // The "sync frequency" preference really just needs to control the frequency value
    683             // in our database.
    684             mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval()));
    685         } else {
    686             // This account only syncs email (not contacts or calendar), which means that we will
    687             // hide the preference to turn syncing on and off. In this case, we want the sync
    688             // frequency preference to also control whether or not syncing is enabled at all. If
    689             // sync is turned off, we will display "sync never" regardless of what the numeric
    690             // value we have stored says.
    691             boolean synced = ContentResolver.getSyncAutomatically(androidAcct,
    692                     EmailContent.AUTHORITY);
    693             if (synced) {
    694                 mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval()));
    695             } else {
    696                 mCheckFrequency.setValue(String.valueOf(Account.CHECK_INTERVAL_NEVER));
    697             }
    698         }
    699         mCheckFrequency.setSummary(mCheckFrequency.getEntry());
    700         mCheckFrequency.setOnPreferenceChangeListener(this);
    701 
    702         final Preference quickResponsePref = findPreference(PREFERENCE_QUICK_RESPONSES);
    703         if (quickResponsePref != null) {
    704             quickResponsePref.setOnPreferenceClickListener(
    705                     new Preference.OnPreferenceClickListener() {
    706                         @Override
    707                         public boolean onPreferenceClick(Preference preference) {
    708                             onEditQuickResponses(mUiAccount);
    709                             return true;
    710                         }
    711                     });
    712         }
    713 
    714         // Add check window preference
    715         final PreferenceCategory dataUsageCategory =
    716                 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE);
    717 
    718         if (mServiceInfo.offerLookback) {
    719             if (mSyncWindow == null) {
    720                 mSyncWindow = new ListPreference(mContext);
    721                 mSyncWindow.setKey(PREFERENCE_SYNC_WINDOW);
    722                 dataUsageCategory.addPreference(mSyncWindow);
    723             }
    724             mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label);
    725             mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback()));
    726             final int maxLookback;
    727             if (mAccount.mPolicy != null) {
    728                 maxLookback = mAccount.mPolicy.mMaxEmailLookback;
    729             } else {
    730                 maxLookback = 0;
    731             }
    732 
    733             MailboxSettings.setupLookbackPreferenceOptions(mContext, mSyncWindow, maxLookback,
    734                     false);
    735 
    736             // Must correspond to the hole in the XML file that's reserved.
    737             mSyncWindow.setOrder(2);
    738             mSyncWindow.setOnPreferenceChangeListener(this);
    739 
    740             if (mSyncSettings == null) {
    741                 mSyncSettings = new Preference(mContext);
    742                 mSyncSettings.setKey(PREFERENCE_SYNC_SETTINGS);
    743                 dataUsageCategory.addPreference(mSyncSettings);
    744             }
    745 
    746             mSyncSettings.setTitle(R.string.folder_sync_settings_pref_title);
    747             mSyncSettings.setOrder(3);
    748         }
    749 
    750         final PreferenceCategory folderPrefs =
    751                 (PreferenceCategory) findPreference(PREFERENCE_SYSTEM_FOLDERS);
    752         if (folderPrefs != null) {
    753             if (mServiceInfo.requiresSetup) {
    754                 Preference trashPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_TRASH);
    755                 Intent i = new Intent(mContext, FolderPickerActivity.class);
    756                 Uri uri = EmailContent.CONTENT_URI.buildUpon().appendQueryParameter(
    757                         "account", Long.toString(mAccount.getId())).build();
    758                 i.setData(uri);
    759                 i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_TRASH);
    760                 trashPreference.setIntent(i);
    761 
    762                 Preference sentPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_SENT);
    763                 i = new Intent(mContext, FolderPickerActivity.class);
    764                 i.setData(uri);
    765                 i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_SENT);
    766                 sentPreference.setIntent(i);
    767             } else {
    768                 getPreferenceScreen().removePreference(folderPrefs);
    769             }
    770         }
    771 
    772         final CheckBoxPreference backgroundAttachments = (CheckBoxPreference)
    773                 findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS);
    774         if (backgroundAttachments != null) {
    775             if (!mServiceInfo.offerAttachmentPreload) {
    776                 dataUsageCategory.removePreference(backgroundAttachments);
    777             } else {
    778                 backgroundAttachments.setChecked(
    779                         0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS));
    780                 backgroundAttachments.setOnPreferenceChangeListener(this);
    781             }
    782         }
    783 
    784         final PreferenceCategory notificationsCategory =
    785                 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS);
    786 
    787         if (mInboxFolderPreferences != null) {
    788             final CheckBoxPreference inboxNotify = (CheckBoxPreference) findPreference(
    789                 FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED);
    790             inboxNotify.setChecked(mInboxFolderPreferences.areNotificationsEnabled());
    791             inboxNotify.setOnPreferenceChangeListener(this);
    792 
    793             mInboxRingtone = findPreference(FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE);
    794             final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri();
    795             if (!TextUtils.isEmpty(ringtoneUri)) {
    796                 mRingtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(ringtoneUri));
    797             }
    798             setRingtoneSummary();
    799             mInboxRingtone.setOnPreferenceChangeListener(this);
    800             mInboxRingtone.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    801                 @Override
    802                 public boolean onPreferenceClick(final Preference preference) {
    803                     showRingtonePicker();
    804 
    805                     return true;
    806                 }
    807             });
    808 
    809             notificationsCategory.setEnabled(true);
    810 
    811             // Set the vibrator value, or hide it on devices w/o a vibrator
    812             mInboxVibrate = (CheckBoxPreference) findPreference(
    813                     FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE);
    814             if (mInboxVibrate != null) {
    815                 mInboxVibrate.setChecked(
    816                         mInboxFolderPreferences.isNotificationVibrateEnabled());
    817                 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
    818                 if (vibrator.hasVibrator()) {
    819                     // When the value is changed, update the setting.
    820                     mInboxVibrate.setOnPreferenceChangeListener(this);
    821                 } else {
    822                     // No vibrator present. Remove the preference altogether.
    823                     notificationsCategory.removePreference(mInboxVibrate);
    824                     mInboxVibrate = null;
    825                 }
    826             }
    827         } else {
    828             notificationsCategory.setEnabled(false);
    829         }
    830 
    831         final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT);
    832         final PreferenceCategory policiesCategory = (PreferenceCategory) findPreference(
    833                 PREFERENCE_CATEGORY_POLICIES);
    834         if (policiesCategory != null) {
    835             // TODO: This code for showing policies isn't working. For KLP, just don't even bother
    836             // showing this data; we'll fix this later.
    837     /*
    838             if (policy != null) {
    839                 if (policy.mProtocolPoliciesEnforced != null) {
    840                     ArrayList<String> policies = getSystemPoliciesList(policy);
    841                     setPolicyListSummary(policies, policy.mProtocolPoliciesEnforced,
    842                             PREFERENCE_POLICIES_ENFORCED);
    843                 }
    844                 if (policy.mProtocolPoliciesUnsupported != null) {
    845                     ArrayList<String> policies = new ArrayList<String>();
    846                     setPolicyListSummary(policies, policy.mProtocolPoliciesUnsupported,
    847                             PREFERENCE_POLICIES_UNSUPPORTED);
    848                 } else {
    849                     // Don't show "retry" unless we have unsupported policies
    850                     policiesCategory.removePreference(retryAccount);
    851                 }
    852             } else {
    853     */
    854             // Remove the category completely if there are no policies
    855             getPreferenceScreen().removePreference(policiesCategory);
    856 
    857             //}
    858         }
    859 
    860         if (retryAccount != null) {
    861             retryAccount.setOnPreferenceClickListener(
    862                     new Preference.OnPreferenceClickListener() {
    863                         @Override
    864                         public boolean onPreferenceClick(Preference preference) {
    865                             // Release the account
    866                             SecurityPolicy.setAccountHoldFlag(mContext, mAccount, false);
    867                             // Remove the preference
    868                             if (policiesCategory != null) {
    869                                 policiesCategory.removePreference(retryAccount);
    870                             }
    871                             return true;
    872                         }
    873                     });
    874         }
    875         findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener(
    876                 new Preference.OnPreferenceClickListener() {
    877                     @Override
    878                     public boolean onPreferenceClick(Preference preference) {
    879                         onIncomingSettings(mAccount);
    880                         return true;
    881                     }
    882                 });
    883 
    884         // Hide the outgoing account setup link if it's not activated
    885         final Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING);
    886         if (prefOutgoing != null) {
    887             if (mServiceInfo.usesSmtp && mAccount.mHostAuthSend != null) {
    888                 prefOutgoing.setOnPreferenceClickListener(
    889                         new Preference.OnPreferenceClickListener() {
    890                             @Override
    891                             public boolean onPreferenceClick(Preference preference) {
    892                                 onOutgoingSettings(mAccount);
    893                                 return true;
    894                             }
    895                         });
    896             } else {
    897                 if (mServiceInfo.usesSmtp) {
    898                     // We really ought to have an outgoing host auth but we don't.
    899                     // There's nothing we can do at this point, so just log the error.
    900                     LogUtils.e(LogUtils.TAG, "Account %d has a bad outbound hostauth",
    901                             mAccount.getId());
    902                 }
    903                 PreferenceCategory serverCategory = (PreferenceCategory) findPreference(
    904                         PREFERENCE_CATEGORY_SERVER);
    905                 serverCategory.removePreference(prefOutgoing);
    906             }
    907         }
    908 
    909         final CheckBoxPreference syncContacts =
    910                 (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS);
    911         final CheckBoxPreference syncCalendar =
    912                 (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR);
    913         final CheckBoxPreference syncEmail =
    914                 (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL);
    915         if (syncContacts != null && syncCalendar != null && syncEmail != null) {
    916             if (mServiceInfo.syncContacts || mServiceInfo.syncCalendar) {
    917                 if (mServiceInfo.syncContacts) {
    918                     syncContacts.setChecked(ContentResolver
    919                             .getSyncAutomatically(androidAcct, ContactsContract.AUTHORITY));
    920                     syncContacts.setOnPreferenceChangeListener(this);
    921                 } else {
    922                     syncContacts.setChecked(false);
    923                     syncContacts.setEnabled(false);
    924                 }
    925                 if (mServiceInfo.syncCalendar) {
    926                     syncCalendar.setChecked(ContentResolver
    927                             .getSyncAutomatically(androidAcct, CalendarContract.AUTHORITY));
    928                     syncCalendar.setOnPreferenceChangeListener(this);
    929                 } else {
    930                     syncCalendar.setChecked(false);
    931                     syncCalendar.setEnabled(false);
    932                 }
    933                 syncEmail.setChecked(ContentResolver
    934                         .getSyncAutomatically(androidAcct, EmailContent.AUTHORITY));
    935                 syncEmail.setOnPreferenceChangeListener(this);
    936             } else {
    937                 dataUsageCategory.removePreference(syncContacts);
    938                 dataUsageCategory.removePreference(syncCalendar);
    939                 dataUsageCategory.removePreference(syncEmail);
    940             }
    941         }
    942     }
    943 
    944     /**
    945      * Shows the system ringtone picker.
    946      */
    947     private void showRingtonePicker() {
    948         Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
    949         final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri();
    950         if (!TextUtils.isEmpty(ringtoneUri)) {
    951             intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(ringtoneUri));
    952         }
    953         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
    954         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
    955                 Settings.System.DEFAULT_NOTIFICATION_URI);
    956         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
    957         intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
    958         startActivityForResult(intent, RINGTONE_REQUEST_CODE);
    959     }
    960 
    961     /**
    962      * Dispatch to edit quick responses.
    963      */
    964     public void onEditQuickResponses(com.android.mail.providers.Account account) {
    965         final Bundle args = AccountSettingsEditQuickResponsesFragment.createArgs(account);
    966         final PreferenceActivity activity = (PreferenceActivity) getActivity();
    967         activity.startPreferencePanel(AccountSettingsEditQuickResponsesFragment.class.getName(),
    968                 args, R.string.account_settings_edit_quick_responses_label, null, null, 0);
    969     }
    970 
    971     /**
    972      * Dispatch to edit incoming settings.
    973      */
    974     public void onIncomingSettings(Account account) {
    975         final Intent intent =
    976                 AccountServerSettingsActivity.getIntentForIncoming(getActivity(), account);
    977         getActivity().startActivity(intent);
    978     }
    979 
    980     /**
    981      * Dispatch to edit outgoing settings.
    982      */
    983     public void onOutgoingSettings(Account account) {
    984         final Intent intent =
    985                 AccountServerSettingsActivity.getIntentForOutgoing(getActivity(), account);
    986         getActivity().startActivity(intent);
    987     }
    988 }
    989