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