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