AccountSettingsFragment.java revision c6953b77552d4cb71776cf0537dc226029381628
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.content.ContentResolver; 21import android.content.ContentValues; 22import android.content.Context; 23import android.content.Intent; 24import android.content.res.Resources; 25import android.database.Cursor; 26import android.media.Ringtone; 27import android.media.RingtoneManager; 28import android.net.Uri; 29import android.os.AsyncTask; 30import android.os.Bundle; 31import android.os.Vibrator; 32import android.preference.CheckBoxPreference; 33import android.preference.EditTextPreference; 34import android.preference.ListPreference; 35import android.preference.Preference; 36import android.preference.PreferenceCategory; 37import android.preference.Preference.OnPreferenceClickListener; 38import android.provider.CalendarContract; 39import android.provider.ContactsContract; 40import android.provider.Settings; 41import android.text.TextUtils; 42import android.view.Menu; 43import android.view.MenuInflater; 44 45import com.android.email.R; 46import com.android.email.SecurityPolicy; 47import com.android.email.provider.EmailProvider; 48import com.android.email.provider.FolderPickerActivity; 49import com.android.email.service.EmailServiceUtils; 50import com.android.email.service.EmailServiceUtils.EmailServiceInfo; 51import com.android.email2.ui.MailActivityEmail; 52import com.android.emailcommon.Logging; 53import com.android.emailcommon.provider.Account; 54import com.android.emailcommon.provider.EmailContent; 55import com.android.emailcommon.provider.HostAuth; 56import com.android.emailcommon.provider.Mailbox; 57import com.android.emailcommon.provider.Policy; 58import com.android.emailcommon.utility.Utility; 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.settings.SettingsUtils; 64import com.android.mail.utils.LogUtils; 65import com.android.mail.utils.NotificationUtils; 66 67import java.util.ArrayList; 68import java.util.HashMap; 69import java.util.Map; 70 71/** 72 * Fragment containing the main logic for account settings. This also calls out to other 73 * fragments for server settings. 74 * 75 * TODO: Remove or make async the mAccountDirty reload logic. Probably no longer needed. 76 * TODO: Can we defer calling addPreferencesFromResource() until after we load the account? This 77 * could reduce flicker. 78 */ 79public class AccountSettingsFragment extends EmailPreferenceFragment 80 implements Preference.OnPreferenceChangeListener { 81 82 // Keys used for arguments bundle 83 private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId"; 84 private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email"; 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_BACKGROUND_ATTACHMENTS = 92 "account_background_attachments"; 93 private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage"; 94 private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications"; 95 private static final String PREFERENCE_CATEGORY_SERVER = "account_servers"; 96 private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies"; 97 private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced"; 98 private static final String PREFERENCE_POLICIES_UNSUPPORTED = "policies_unsupported"; 99 private static final String PREFERENCE_POLICIES_RETRY_ACCOUNT = "policies_retry_account"; 100 private static final String PREFERENCE_INCOMING = "incoming"; 101 private static final String PREFERENCE_OUTGOING = "outgoing"; 102 private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts"; 103 private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar"; 104 private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email"; 105 106 private static final String PREFERENCE_SYSTEM_FOLDERS = "system_folders"; 107 private static final String PREFERENCE_SYSTEM_FOLDERS_TRASH = "system_folders_trash"; 108 private static final String PREFERENCE_SYSTEM_FOLDERS_SENT = "system_folders_sent"; 109 110 // Request code to start different activities. 111 private static final int RINGTONE_REQUEST_CODE = 0; 112 113 private EditTextPreference mAccountDescription; 114 private EditTextPreference mAccountName; 115 private EditTextPreference mAccountSignature; 116 private ListPreference mCheckFrequency; 117 private ListPreference mSyncWindow; 118 private CheckBoxPreference mAccountBackgroundAttachments; 119 private CheckBoxPreference mInboxNotify; 120 private CheckBoxPreference mInboxVibrate; 121 private Preference mInboxRingtone; 122 private PreferenceCategory mNotificationsCategory; 123 private CheckBoxPreference mSyncContacts; 124 private CheckBoxPreference mSyncCalendar; 125 private CheckBoxPreference mSyncEmail; 126 127 private Context mContext; 128 129 /** 130 * mAccount is email-specific, transition to using mUiAccount instead 131 */ 132 @Deprecated 133 private Account mAccount; 134 private boolean mAccountDirty; 135 private com.android.mail.providers.Account mUiAccount; 136 private Callback mCallback = EmptyCallback.INSTANCE; 137 private boolean mStarted; 138 private boolean mLoaded; 139 private boolean mSaveOnExit; 140 141 private Ringtone mRingtone; 142 143 private AccountPreferences mAccountPreferences; 144 private FolderPreferences mInboxFolderPreferences; 145 146 /** The e-mail of the account being edited. */ 147 private String mAccountEmail; 148 149 // Async Tasks 150 private AsyncTask<?,?,?> mLoadAccountTask; 151 152 /** 153 * Callback interface that owning activities must provide 154 */ 155 public interface Callback { 156 public void onSettingsChanged(Account account, String preference, Object value); 157 public void onEditQuickResponses(com.android.mail.providers.Account account); 158 public void onIncomingSettings(Account account); 159 public void onOutgoingSettings(Account account); 160 public void abandonEdit(); 161 } 162 163 private static class EmptyCallback implements Callback { 164 public static final Callback INSTANCE = new EmptyCallback(); 165 @Override public void onSettingsChanged(Account account, String preference, Object value) {} 166 @Override public void onEditQuickResponses(com.android.mail.providers.Account account) {} 167 @Override public void onIncomingSettings(Account account) {} 168 @Override public void onOutgoingSettings(Account account) {} 169 @Override public void abandonEdit() {} 170 } 171 172 /** 173 * If launching with an arguments bundle, use this method to build the arguments. 174 */ 175 public static Bundle buildArguments(long accountId, String email) { 176 Bundle b = new Bundle(); 177 b.putLong(BUNDLE_KEY_ACCOUNT_ID, accountId); 178 b.putString(BUNDLE_KEY_ACCOUNT_EMAIL, email); 179 return b; 180 } 181 182 public static String getTitleFromArgs(Bundle args) { 183 return (args == null) ? null : args.getString(BUNDLE_KEY_ACCOUNT_EMAIL); 184 } 185 186 @Override 187 public void onAttach(Activity activity) { 188 super.onAttach(activity); 189 mContext = activity; 190 } 191 192 /** 193 * Called to do initial creation of a fragment. This is called after 194 * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. 195 */ 196 @Override 197 public void onCreate(Bundle savedInstanceState) { 198 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 199 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onCreate"); 200 } 201 super.onCreate(savedInstanceState); 202 203 // Load the preferences from an XML resource 204 addPreferencesFromResource(R.xml.account_settings_preferences); 205 206 // Start loading the account data, if provided in the arguments 207 // If not, activity must call startLoadingAccount() directly 208 Bundle b = getArguments(); 209 if (b != null) { 210 long accountId = b.getLong(BUNDLE_KEY_ACCOUNT_ID, -1); 211 mAccountEmail = b.getString(BUNDLE_KEY_ACCOUNT_EMAIL); 212 if (accountId >= 0 && !mLoaded) { 213 startLoadingAccount(accountId); 214 } 215 } 216 217 mAccountDirty = false; 218 } 219 220 @Override 221 public void onActivityCreated(Bundle savedInstanceState) { 222 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 223 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onActivityCreated"); 224 } 225 super.onActivityCreated(savedInstanceState); 226 } 227 228 /** 229 * Called when the Fragment is visible to the user. 230 */ 231 @Override 232 public void onStart() { 233 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 234 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onStart"); 235 } 236 super.onStart(); 237 mStarted = true; 238 239 // If the loaded account is ready now, load the UI 240 if (mAccount != null && !mLoaded) { 241 loadSettings(); 242 } 243 } 244 245 /** 246 * Called when the fragment is visible to the user and actively running. 247 * TODO: Don't read account data on UI thread. This should be fixed by removing the need 248 * to do this, not by spinning up yet another thread. 249 */ 250 @Override 251 public void onResume() { 252 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 253 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onResume"); 254 } 255 super.onResume(); 256 257 if (mAccountDirty) { 258 // if we are coming back from editing incoming or outgoing settings, 259 // we need to refresh them here so we don't accidentally overwrite the 260 // old values we're still holding here 261 mAccount.mHostAuthRecv = 262 HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv); 263 mAccount.mHostAuthSend = 264 HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeySend); 265 // Because "delete policy" UI is on edit incoming settings, we have 266 // to refresh that as well. 267 Account refreshedAccount = Account.restoreAccountWithId(mContext, mAccount.mId); 268 if (refreshedAccount == null || mAccount.mHostAuthRecv == null) { 269 mSaveOnExit = false; 270 mCallback.abandonEdit(); 271 return; 272 } 273 mAccount.setDeletePolicy(refreshedAccount.getDeletePolicy()); 274 mAccountDirty = false; 275 } 276 } 277 278 @Override 279 public void onPause() { 280 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 281 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onPause"); 282 } 283 super.onPause(); 284 if (mSaveOnExit) { 285 saveSettings(); 286 } 287 } 288 289 /** 290 * Called when the Fragment is no longer started. 291 */ 292 @Override 293 public void onStop() { 294 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 295 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onStop"); 296 } 297 super.onStop(); 298 mStarted = false; 299 } 300 301 @Override 302 public void onActivityResult(int requestCode, int resultCode, Intent data) { 303 switch (requestCode) { 304 case RINGTONE_REQUEST_CODE: 305 if (resultCode == Activity.RESULT_OK && data != null) { 306 Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); 307 setRingtone(uri); 308 } 309 break; 310 } 311 } 312 313 /** 314 * Sets the current ringtone. 315 */ 316 private void setRingtone(Uri ringtone) { 317 if (ringtone != null) { 318 mInboxFolderPreferences.setNotificationRingtoneUri(ringtone.toString()); 319 mRingtone = RingtoneManager.getRingtone(getActivity(), ringtone); 320 } else { 321 // Null means silent was selected. 322 mInboxFolderPreferences.setNotificationRingtoneUri(""); 323 mRingtone = null; 324 } 325 326 setRingtoneSummary(); 327 } 328 329 private void setRingtoneSummary() { 330 final String summary = mRingtone != null ? mRingtone.getTitle(mContext) 331 : mContext.getString(R.string.silent_ringtone); 332 333 mInboxRingtone.setSummary(summary); 334 } 335 336 /** 337 * Listen to all preference changes in this class. 338 * @param preference 339 * @param newValue 340 * @return 341 */ 342 @Override 343 public boolean onPreferenceChange(Preference preference, Object newValue){ 344 // Can't use a switch here. Falling back to a giant conditional. 345 final String key = preference.getKey(); 346 if (key.equals(PREFERENCE_DESCRIPTION)){ 347 String summary = newValue.toString().trim(); 348 if (TextUtils.isEmpty(summary)) { 349 summary = mAccount.mEmailAddress; 350 } 351 mAccountDescription.setSummary(summary); 352 mAccountDescription.setText(summary); 353 preferenceChanged(PREFERENCE_DESCRIPTION, summary); 354 return false; 355 } else if (key.equals(PREFERENCE_FREQUENCY)) { 356 final String summary = newValue.toString(); 357 final int index = mCheckFrequency.findIndexOfValue(summary); 358 mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]); 359 mCheckFrequency.setValue(summary); 360 preferenceChanged(PREFERENCE_FREQUENCY, newValue); 361 return false; 362 } else if (key.equals(PREFERENCE_SIGNATURE)) { 363 // Clean up signature if it's only whitespace (which is easy to do on a 364 // soft keyboard) but leave whitespace in place otherwise, to give the user 365 // maximum flexibility, e.g. the ability to indent 366 String signature = newValue.toString(); 367 if (signature.trim().isEmpty()) { 368 signature = ""; 369 } 370 mAccountSignature.setText(signature); 371 SettingsUtils.updatePreferenceSummary(mAccountSignature, signature, 372 R.string.preferences_signature_summary_not_set); 373 preferenceChanged(PREFERENCE_SIGNATURE, signature); 374 return false; 375 } else if (key.equals(PREFERENCE_NAME)) { 376 final String summary = newValue.toString().trim(); 377 if (!TextUtils.isEmpty(summary)) { 378 mAccountName.setSummary(summary); 379 mAccountName.setText(summary); 380 preferenceChanged(PREFERENCE_NAME, summary); 381 } 382 return false; 383 } else if (FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE.equals(key)) { 384 final boolean vibrateSetting = (Boolean) newValue; 385 mInboxVibrate.setChecked(vibrateSetting); 386 mInboxFolderPreferences.setNotificationVibrateEnabled(vibrateSetting); 387 preferenceChanged(FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE, newValue); 388 return true; 389 } else if (FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED.equals(key)) { 390 mInboxFolderPreferences.setNotificationsEnabled((Boolean) newValue); 391 preferenceChanged(FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED, newValue); 392 return true; 393 } else { 394 // Default behavior, just indicate that the preferences were written 395 preferenceChanged(key, newValue); 396 return true; 397 } 398 } 399 400 /** 401 * Called when the fragment is no longer in use. 402 */ 403 @Override 404 public void onDestroy() { 405 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 406 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy"); 407 } 408 super.onDestroy(); 409 410 Utility.cancelTaskInterrupt(mLoadAccountTask); 411 mLoadAccountTask = null; 412 } 413 414 @Override 415 public void onSaveInstanceState(Bundle outState) { 416 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 417 LogUtils.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState"); 418 } 419 super.onSaveInstanceState(outState); 420 } 421 422 @Override 423 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 424 menu.clear(); 425 inflater.inflate(R.menu.settings_fragment_menu, menu); 426 } 427 428 /** 429 * Activity provides callbacks here 430 */ 431 public void setCallback(Callback callback) { 432 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 433 } 434 435 /** 436 * Start loading a single account in preparation for editing it 437 */ 438 public void startLoadingAccount(long accountId) { 439 Utility.cancelTaskInterrupt(mLoadAccountTask); 440 mLoadAccountTask = new LoadAccountTask().executeOnExecutor( 441 AsyncTask.THREAD_POOL_EXECUTOR, accountId); 442 } 443 444 /** 445 * Async task to load account in order to view/edit it 446 */ 447 private class LoadAccountTask extends AsyncTask<Long, Void, Map<String, Object>> { 448 static final String ACCOUNT_KEY = "account"; 449 static final String UI_ACCOUNT_KEY = "uiAccount"; 450 451 @Override 452 protected Map<String, Object> doInBackground(Long... params) { 453 final long accountId = params[0]; 454 Account account = Account.restoreAccountWithId(mContext, accountId); 455 if (account != null) { 456 account.mHostAuthRecv = 457 HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv); 458 account.mHostAuthSend = 459 HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend); 460 if (account.mHostAuthRecv == null) { 461 account = null; 462 } 463 } 464 465 final Cursor accountCursor = mContext.getContentResolver().query(EmailProvider 466 .uiUri("uiaccount", accountId), UIProvider.ACCOUNTS_PROJECTION, null, 467 null, null); 468 469 final com.android.mail.providers.Account uiAccount; 470 try { 471 if (accountCursor != null && accountCursor.moveToFirst()) { 472 uiAccount = new com.android.mail.providers.Account(accountCursor); 473 } else { 474 uiAccount = null; 475 } 476 } finally { 477 if (accountCursor != null) { 478 accountCursor.close(); 479 } 480 } 481 482 final Map<String, Object> map = new HashMap<String, Object>(2); 483 map.put(ACCOUNT_KEY, account); 484 map.put(UI_ACCOUNT_KEY, uiAccount); 485 return map; 486 } 487 488 @Override 489 protected void onPostExecute(Map<String, Object> map) { 490 if (!isCancelled()) { 491 final Account account = (Account) map.get(ACCOUNT_KEY); 492 if (account == null) { 493 mSaveOnExit = false; 494 mCallback.abandonEdit(); 495 } else { 496 mAccount = account; 497 if (mStarted && !mLoaded) { 498 loadSettings(); 499 } 500 } 501 mUiAccount = (com.android.mail.providers.Account) map.get(UI_ACCOUNT_KEY); 502 } 503 } 504 } 505 506 /** 507 * From a Policy, create and return an ArrayList of Strings that describe (simply) those 508 * policies that are supported by the OS. At the moment, the strings are simple (e.g. 509 * "password required"); we should probably add more information (# characters, etc.), though 510 */ 511 private ArrayList<String> getSystemPoliciesList(Policy policy) { 512 Resources res = mContext.getResources(); 513 ArrayList<String> policies = new ArrayList<String>(); 514 if (policy.mPasswordMode != Policy.PASSWORD_MODE_NONE) { 515 policies.add(res.getString(R.string.policy_require_password)); 516 } 517 if (policy.mPasswordHistory > 0) { 518 policies.add(res.getString(R.string.policy_password_history)); 519 } 520 if (policy.mPasswordExpirationDays > 0) { 521 policies.add(res.getString(R.string.policy_password_expiration)); 522 } 523 if (policy.mMaxScreenLockTime > 0) { 524 policies.add(res.getString(R.string.policy_screen_timeout)); 525 } 526 if (policy.mDontAllowCamera) { 527 policies.add(res.getString(R.string.policy_dont_allow_camera)); 528 } 529 if (policy.mMaxEmailLookback != 0) { 530 policies.add(res.getString(R.string.policy_email_age)); 531 } 532 if (policy.mMaxCalendarLookback != 0) { 533 policies.add(res.getString(R.string.policy_calendar_age)); 534 } 535 return policies; 536 } 537 538 private void setPolicyListSummary(ArrayList<String> policies, String policiesToAdd, 539 String preferenceName) { 540 Policy.addPolicyStringToList(policiesToAdd, policies); 541 if (policies.size() > 0) { 542 Preference p = findPreference(preferenceName); 543 StringBuilder sb = new StringBuilder(); 544 for (String desc: policies) { 545 sb.append(desc); 546 sb.append('\n'); 547 } 548 p.setSummary(sb.toString()); 549 } 550 } 551 552 /** 553 * Loads settings that are dependent on a {@link com.android.mail.providers.Account}, which 554 * must be obtained off the main thread. 555 */ 556 private void loadSettingsOffMainThread() { 557 new Thread(new Runnable() { 558 @Override 559 public void run() { 560 if (mUiAccount == null) { 561 return; 562 } 563 final Cursor folderCursor = mContext.getContentResolver().query( 564 mUiAccount.settings.defaultInbox, UIProvider.FOLDERS_PROJECTION, null, null, 565 null); 566 if (folderCursor == null) { 567 return; 568 } 569 570 final Folder folder; 571 try { 572 if (folderCursor.moveToFirst()) { 573 folder = new Folder(folderCursor); 574 } else { 575 return; 576 } 577 } finally { 578 folderCursor.close(); 579 } 580 581 mAccountPreferences = new AccountPreferences(mContext, mUiAccount.name); 582 mInboxFolderPreferences = 583 new FolderPreferences(mContext, mUiAccount.name, folder, true); 584 585 NotificationUtils.moveNotificationSetting( 586 mAccountPreferences, mInboxFolderPreferences); 587 588 final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri(); 589 if (!TextUtils.isEmpty(ringtoneUri)) { 590 mRingtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(ringtoneUri)); 591 } 592 593 final Activity activity = getActivity(); 594 if (activity != null) { 595 activity.runOnUiThread(new Runnable() { 596 @Override 597 public void run() { 598 mInboxNotify.setChecked( 599 mInboxFolderPreferences.areNotificationsEnabled()); 600 mInboxVibrate.setChecked( 601 mInboxFolderPreferences.isNotificationVibrateEnabled()); 602 setRingtoneSummary(); 603 // Notification preferences must be disabled until after 604 // mInboxFolderPreferences is available, so enable them here. 605 mNotificationsCategory.setEnabled(true); 606 } 607 }); 608 } 609 } 610 }).start(); 611 } 612 613 /** 614 * Load account data into preference UI. This must be called on the main thread. 615 */ 616 private void loadSettings() { 617 // We can only do this once, so prevent repeat 618 mLoaded = true; 619 // Once loaded the data is ready to be saved, as well 620 mSaveOnExit = false; 621 622 loadSettingsOffMainThread(); 623 624 mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION); 625 mAccountDescription.setSummary(mAccount.getDisplayName()); 626 mAccountDescription.setText(mAccount.getDisplayName()); 627 mAccountDescription.setOnPreferenceChangeListener(this); 628 629 mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME); 630 String senderName = mAccount.getSenderName(); 631 // In rare cases, sendername will be null; Change this to empty string to avoid NPE's 632 if (senderName == null) senderName = ""; 633 mAccountName.setSummary(senderName); 634 mAccountName.setText(senderName); 635 mAccountName.setOnPreferenceChangeListener(this); 636 637 final String accountSignature = mAccount.getSignature(); 638 mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE); 639 mAccountSignature.setText(accountSignature); 640 mAccountSignature.setOnPreferenceChangeListener(this); 641 SettingsUtils.updatePreferenceSummary(mAccountSignature, accountSignature, 642 R.string.preferences_signature_summary_not_set); 643 644 645 mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); 646 final String protocol = Account.getProtocol(mContext, mAccount.mId); 647 final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext, protocol); 648 mCheckFrequency.setEntries(info.syncIntervalStrings); 649 mCheckFrequency.setEntryValues(info.syncIntervals); 650 mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval())); 651 mCheckFrequency.setSummary(mCheckFrequency.getEntry()); 652 mCheckFrequency.setOnPreferenceChangeListener(this); 653 654 findPreference(PREFERENCE_QUICK_RESPONSES).setOnPreferenceClickListener( 655 new Preference.OnPreferenceClickListener() { 656 @Override 657 public boolean onPreferenceClick(Preference preference) { 658 mAccountDirty = true; 659 mCallback.onEditQuickResponses(mUiAccount); 660 return true; 661 } 662 }); 663 664 // Add check window preference 665 PreferenceCategory dataUsageCategory = 666 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE); 667 668 final Policy policy; 669 if (mAccount.mPolicyKey != 0) { 670 // Make sure we have most recent data from account 671 mAccount.refresh(mContext); 672 policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 673 if (policy == null) { 674 // The account has been deleted? Crazy, but not impossible 675 return; 676 } 677 } else { 678 policy = null; 679 } 680 681 mSyncWindow = null; 682 if (info.offerLookback) { 683 mSyncWindow = new ListPreference(mContext); 684 mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label); 685 mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback())); 686 final int maxLookback; 687 if (policy != null) { 688 maxLookback = policy.mMaxEmailLookback; 689 } else { 690 maxLookback = 0; 691 } 692 693 MailboxSettings.setupLookbackPreferenceOptions(mContext, mSyncWindow, maxLookback, 694 false); 695 696 // Must correspond to the hole in the XML file that's reserved. 697 mSyncWindow.setOrder(2); 698 mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 699 @Override 700 public boolean onPreferenceChange(Preference preference, Object newValue) { 701 final String summary = newValue.toString(); 702 int index = mSyncWindow.findIndexOfValue(summary); 703 mSyncWindow.setSummary(mSyncWindow.getEntries()[index]); 704 mSyncWindow.setValue(summary); 705 preferenceChanged(preference.getKey(), newValue); 706 return false; 707 } 708 }); 709 dataUsageCategory.addPreference(mSyncWindow); 710 } 711 712 PreferenceCategory folderPrefs = 713 (PreferenceCategory) findPreference(PREFERENCE_SYSTEM_FOLDERS); 714 if (info.requiresSetup) { 715 Preference trashPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_TRASH); 716 Intent i = new Intent(mContext, FolderPickerActivity.class); 717 Uri uri = EmailContent.CONTENT_URI.buildUpon().appendQueryParameter( 718 "account", Long.toString(mAccount.mId)).build(); 719 i.setData(uri); 720 i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_TRASH); 721 trashPreference.setIntent(i); 722 723 Preference sentPreference = findPreference(PREFERENCE_SYSTEM_FOLDERS_SENT); 724 i = new Intent(mContext, FolderPickerActivity.class); 725 i.setData(uri); 726 i.putExtra(FolderPickerActivity.MAILBOX_TYPE_EXTRA, Mailbox.TYPE_SENT); 727 sentPreference.setIntent(i); 728 } else { 729 getPreferenceScreen().removePreference(folderPrefs); 730 } 731 732 mAccountBackgroundAttachments = (CheckBoxPreference) 733 findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS); 734 if (!info.offerAttachmentPreload) { 735 dataUsageCategory.removePreference(mAccountBackgroundAttachments); 736 } else { 737 mAccountBackgroundAttachments.setChecked( 738 0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS)); 739 mAccountBackgroundAttachments.setOnPreferenceChangeListener(this); 740 } 741 742 mInboxNotify = (CheckBoxPreference) findPreference( 743 FolderPreferences.PreferenceKeys.NOTIFICATIONS_ENABLED); 744 mInboxNotify.setOnPreferenceChangeListener(this); 745 746 mInboxRingtone = findPreference(FolderPreferences.PreferenceKeys.NOTIFICATION_RINGTONE); 747 mInboxRingtone.setOnPreferenceChangeListener(this); 748 mInboxRingtone.setOnPreferenceClickListener(new OnPreferenceClickListener() { 749 @Override 750 public boolean onPreferenceClick(final Preference preference) { 751 showRingtonePicker(); 752 753 return true; 754 } 755 }); 756 757 mNotificationsCategory = 758 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS); 759 760 // Set the vibrator value, or hide it on devices w/o a vibrator 761 mInboxVibrate = (CheckBoxPreference) findPreference( 762 FolderPreferences.PreferenceKeys.NOTIFICATION_VIBRATE); 763 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); 764 if (vibrator.hasVibrator()) { 765 // Checked state will be set when we obtain it in #loadSettingsOffMainThread() 766 767 // When the value is changed, update the setting. 768 mInboxVibrate.setOnPreferenceChangeListener(this); 769 } else { 770 // No vibrator present. Remove the preference altogether. 771 mNotificationsCategory.removePreference(mInboxVibrate); 772 } 773 774 final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT); 775 final PreferenceCategory policiesCategory = (PreferenceCategory) findPreference( 776 PREFERENCE_CATEGORY_POLICIES); 777 if (policy != null) { 778 if (policy.mProtocolPoliciesEnforced != null) { 779 ArrayList<String> policies = getSystemPoliciesList(policy); 780 setPolicyListSummary(policies, policy.mProtocolPoliciesEnforced, 781 PREFERENCE_POLICIES_ENFORCED); 782 } 783 if (policy.mProtocolPoliciesUnsupported != null) { 784 ArrayList<String> policies = new ArrayList<String>(); 785 setPolicyListSummary(policies, policy.mProtocolPoliciesUnsupported, 786 PREFERENCE_POLICIES_UNSUPPORTED); 787 } else { 788 // Don't show "retry" unless we have unsupported policies 789 policiesCategory.removePreference(retryAccount); 790 } 791 } else { 792 // Remove the category completely if there are no policies 793 getPreferenceScreen().removePreference(policiesCategory); 794 } 795 796 retryAccount.setOnPreferenceClickListener( 797 new Preference.OnPreferenceClickListener() { 798 @Override 799 public boolean onPreferenceClick(Preference preference) { 800 // Release the account 801 SecurityPolicy.setAccountHoldFlag(mContext, mAccount, false); 802 // Remove the preference 803 policiesCategory.removePreference(retryAccount); 804 return true; 805 } 806 }); 807 findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener( 808 new Preference.OnPreferenceClickListener() { 809 @Override 810 public boolean onPreferenceClick(Preference preference) { 811 mAccountDirty = true; 812 mCallback.onIncomingSettings(mAccount); 813 return true; 814 } 815 }); 816 817 // Hide the outgoing account setup link if it's not activated 818 Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING); 819 if (info.usesSmtp && mAccount.mHostAuthSend != null) { 820 prefOutgoing.setOnPreferenceClickListener( 821 new Preference.OnPreferenceClickListener() { 822 @Override 823 public boolean onPreferenceClick(Preference preference) { 824 mAccountDirty = true; 825 mCallback.onOutgoingSettings(mAccount); 826 return true; 827 } 828 }); 829 } else { 830 if (info.usesSmtp) { 831 // We really ought to have an outgoing host auth but we don't. 832 // There's nothing we can do at this point, so just log the error. 833 LogUtils.e(Logging.LOG_TAG, "Account %d has a bad outbound hostauth", mAccount.mId); 834 } 835 PreferenceCategory serverCategory = (PreferenceCategory) findPreference( 836 PREFERENCE_CATEGORY_SERVER); 837 serverCategory.removePreference(prefOutgoing); 838 } 839 840 mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS); 841 mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR); 842 mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL); 843 if (info.syncContacts || info.syncCalendar) { 844 android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress, 845 info.accountType); 846 if (info.syncContacts) { 847 mSyncContacts.setChecked(ContentResolver 848 .getSyncAutomatically(acct, ContactsContract.AUTHORITY)); 849 mSyncContacts.setOnPreferenceChangeListener(this); 850 } else { 851 mSyncContacts.setChecked(false); 852 mSyncContacts.setEnabled(false); 853 } 854 if (info.syncCalendar) { 855 mSyncCalendar.setChecked(ContentResolver 856 .getSyncAutomatically(acct, CalendarContract.AUTHORITY)); 857 mSyncCalendar.setOnPreferenceChangeListener(this); 858 } else { 859 mSyncCalendar.setChecked(false); 860 mSyncCalendar.setEnabled(false); 861 } 862 mSyncEmail.setChecked(ContentResolver 863 .getSyncAutomatically(acct, EmailContent.AUTHORITY)); 864 mSyncEmail.setOnPreferenceChangeListener(this); 865 } else { 866 dataUsageCategory.removePreference(mSyncContacts); 867 dataUsageCategory.removePreference(mSyncCalendar); 868 dataUsageCategory.removePreference(mSyncEmail); 869 } 870 } 871 872 /** 873 * Called any time a preference is changed. 874 */ 875 private void preferenceChanged(String preference, Object value) { 876 mCallback.onSettingsChanged(mAccount, preference, value); 877 mSaveOnExit = true; 878 } 879 880 /* 881 * Note: This writes the settings on the UI thread. This has to be done so the settings are 882 * committed before we might be killed. 883 */ 884 private void saveSettings() { 885 // Turn off all controlled flags - will turn them back on while checking UI elements 886 int newFlags = mAccount.getFlags() & ~(Account.FLAGS_BACKGROUND_ATTACHMENTS); 887 888 newFlags |= mAccountBackgroundAttachments.isChecked() ? 889 Account.FLAGS_BACKGROUND_ATTACHMENTS : 0; 890 // If the display name has been cleared, we'll reset it to the default value (email addr) 891 mAccount.setDisplayName(mAccountDescription.getText().trim()); 892 // The sender name must never be empty (this is enforced by the preference editor) 893 mAccount.setSenderName(mAccountName.getText().trim()); 894 mAccount.setSignature(mAccountSignature.getText()); 895 mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue())); 896 if (mSyncWindow != null) { 897 mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue())); 898 } 899 mAccount.setFlags(newFlags); 900 901 EmailServiceInfo info = 902 EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext)); 903 if (info.syncContacts || info.syncCalendar) { 904 android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress, 905 info.accountType); 906 ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY, 907 mSyncContacts.isChecked()); 908 ContentResolver.setSyncAutomatically(acct, CalendarContract.AUTHORITY, 909 mSyncCalendar.isChecked()); 910 ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY, 911 mSyncEmail.isChecked()); 912 } 913 914 // Commit the changes 915 // Note, this is done in the UI thread because at this point, we must commit 916 // all changes - any time after onPause completes, we could be killed. This is analogous 917 // to the way that SharedPreferences tries to work off-thread in apply(), but will pause 918 // until completion in onPause(). 919 ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount); 920 mAccount.update(mContext, cv); 921 922 // Run the remaining changes off-thread 923 MailActivityEmail.setServicesEnabledAsync(mContext); 924 } 925 926 public String getAccountEmail() { 927 // Get the e-mail address of the account being editted, if this is for an existing account. 928 return mAccountEmail; 929 } 930 931 /** 932 * Shows the system ringtone picker. 933 */ 934 private void showRingtonePicker() { 935 Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); 936 final String ringtoneUri = mInboxFolderPreferences.getNotificationRingtoneUri(); 937 if (!TextUtils.isEmpty(ringtoneUri)) { 938 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(ringtoneUri)); 939 } 940 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); 941 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, 942 Settings.System.DEFAULT_NOTIFICATION_URI); 943 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); 944 intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); 945 startActivityForResult(intent, RINGTONE_REQUEST_CODE); 946 } 947} 948