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