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