AccountSettingsFragment.java revision cc0185f07c9198008d8dc685ae9979f3e35e8539
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.SharedPreferences; 24import android.content.res.Resources; 25import android.os.AsyncTask; 26import android.os.Bundle; 27import android.os.Vibrator; 28import android.preference.CheckBoxPreference; 29import android.preference.EditTextPreference; 30import android.preference.ListPreference; 31import android.preference.Preference; 32import android.preference.PreferenceCategory; 33import android.preference.RingtonePreference; 34import android.provider.CalendarContract; 35import android.provider.ContactsContract; 36import android.text.TextUtils; 37import android.util.Log; 38 39import com.android.email.R; 40import com.android.email.SecurityPolicy; 41import com.android.email.service.EmailServiceUtils; 42import com.android.email.service.EmailServiceUtils.EmailServiceInfo; 43import com.android.email2.ui.MailActivityEmail; 44import com.android.emailcommon.AccountManagerTypes; 45import com.android.emailcommon.Logging; 46import com.android.emailcommon.provider.Account; 47import com.android.emailcommon.provider.EmailContent; 48import com.android.emailcommon.provider.HostAuth; 49import com.android.emailcommon.provider.Policy; 50import com.android.emailcommon.utility.Utility; 51 52import java.util.ArrayList; 53 54/** 55 * Fragment containing the main logic for account settings. This also calls out to other 56 * fragments for server settings. 57 * 58 * TODO: Remove or make async the mAccountDirty reload logic. Probably no longer needed. 59 * TODO: Can we defer calling addPreferencesFromResource() until after we load the account? This 60 * could reduce flicker. 61 */ 62public class AccountSettingsFragment extends EmailPreferenceFragment 63 implements Preference.OnPreferenceChangeListener { 64 65 // Keys used for arguments bundle 66 private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId"; 67 private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email"; 68 69 public static final String PREFERENCE_DESCRIPTION = "account_description"; 70 private static final String PREFERENCE_NAME = "account_name"; 71 private static final String PREFERENCE_SIGNATURE = "account_signature"; 72 private static final String PREFERENCE_QUICK_RESPONSES = "account_quick_responses"; 73 private static final String PREFERENCE_FREQUENCY = "account_check_frequency"; 74 private static final String PREFERENCE_BACKGROUND_ATTACHMENTS = 75 "account_background_attachments"; 76 private static final String PREFERENCE_DEFAULT = "account_default"; 77 private static final String PREFERENCE_CATEGORY_DATA_USAGE = "data_usage"; 78 private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications"; 79 private static final String PREFERENCE_NOTIFY = "account_notify"; 80 private static final String PREFERENCE_VIBRATE_WHEN = "account_settings_vibrate_when"; 81 private static final String PREFERENCE_RINGTONE = "account_ringtone"; 82 private static final String PREFERENCE_CATEGORY_SERVER = "account_servers"; 83 private static final String PREFERENCE_CATEGORY_POLICIES = "account_policies"; 84 private static final String PREFERENCE_POLICIES_ENFORCED = "policies_enforced"; 85 private static final String PREFERENCE_POLICIES_UNSUPPORTED = "policies_unsupported"; 86 private static final String PREFERENCE_POLICIES_RETRY_ACCOUNT = "policies_retry_account"; 87 private static final String PREFERENCE_INCOMING = "incoming"; 88 private static final String PREFERENCE_OUTGOING = "outgoing"; 89 private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts"; 90 private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar"; 91 private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email"; 92 93 // These strings must match account_settings_vibrate_when_* strings in strings.xml 94 private static final String PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS = "always"; 95 private static final String PREFERENCE_VALUE_VIBRATE_WHEN_SILENT = "silent"; 96 private static final String PREFERENCE_VALUE_VIBRATE_WHEN_NEVER = "never"; 97 98 private EditTextPreference mAccountDescription; 99 private EditTextPreference mAccountName; 100 private EditTextPreference mAccountSignature; 101 private ListPreference mCheckFrequency; 102 private ListPreference mSyncWindow; 103 private CheckBoxPreference mAccountBackgroundAttachments; 104 private CheckBoxPreference mAccountDefault; 105 private CheckBoxPreference mAccountNotify; 106 private ListPreference mAccountVibrateWhen; 107 private RingtonePreference mAccountRingtone; 108 private CheckBoxPreference mSyncContacts; 109 private CheckBoxPreference mSyncCalendar; 110 private CheckBoxPreference mSyncEmail; 111 112 private Context mContext; 113 private Account mAccount; 114 private boolean mAccountDirty; 115 private long mDefaultAccountId; 116 private Callback mCallback = EmptyCallback.INSTANCE; 117 private boolean mStarted; 118 private boolean mLoaded; 119 private boolean mSaveOnExit; 120 121 /** The e-mail of the account being edited. */ 122 private String mAccountEmail; 123 124 // Async Tasks 125 private AsyncTask<?,?,?> mLoadAccountTask; 126 127 /** 128 * Callback interface that owning activities must provide 129 */ 130 public interface Callback { 131 public void onSettingsChanged(Account account, String preference, Object value); 132 public void onEditQuickResponses(Account account); 133 public void onIncomingSettings(Account account); 134 public void onOutgoingSettings(Account account); 135 public void abandonEdit(); 136 } 137 138 private static class EmptyCallback implements Callback { 139 public static final Callback INSTANCE = new EmptyCallback(); 140 @Override public void onSettingsChanged(Account account, String preference, Object value) {} 141 @Override public void onEditQuickResponses(Account account) {} 142 @Override public void onIncomingSettings(Account account) {} 143 @Override public void onOutgoingSettings(Account account) {} 144 @Override public void abandonEdit() {} 145 } 146 147 /** 148 * If launching with an arguments bundle, use this method to build the arguments. 149 */ 150 public static Bundle buildArguments(long accountId, String email) { 151 Bundle b = new Bundle(); 152 b.putLong(BUNDLE_KEY_ACCOUNT_ID, accountId); 153 b.putString(BUNDLE_KEY_ACCOUNT_EMAIL, email); 154 return b; 155 } 156 157 public static String getTitleFromArgs(Bundle args) { 158 return (args == null) ? null : args.getString(BUNDLE_KEY_ACCOUNT_EMAIL); 159 } 160 161 @Override 162 public void onAttach(Activity activity) { 163 super.onAttach(activity); 164 mContext = activity; 165 } 166 167 /** 168 * Called to do initial creation of a fragment. This is called after 169 * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}. 170 */ 171 @Override 172 public void onCreate(Bundle savedInstanceState) { 173 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 174 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onCreate"); 175 } 176 super.onCreate(savedInstanceState); 177 178 // Load the preferences from an XML resource 179 addPreferencesFromResource(R.xml.account_settings_preferences); 180 181 // Start loading the account data, if provided in the arguments 182 // If not, activity must call startLoadingAccount() directly 183 Bundle b = getArguments(); 184 if (b != null) { 185 long accountId = b.getLong(BUNDLE_KEY_ACCOUNT_ID, -1); 186 mAccountEmail = b.getString(BUNDLE_KEY_ACCOUNT_EMAIL); 187 if (accountId >= 0 && !mLoaded) { 188 startLoadingAccount(accountId); 189 } 190 } 191 192 mAccountDirty = false; 193 } 194 195 @Override 196 public void onActivityCreated(Bundle savedInstanceState) { 197 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 198 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onActivityCreated"); 199 } 200 super.onActivityCreated(savedInstanceState); 201 } 202 203 /** 204 * Called when the Fragment is visible to the user. 205 */ 206 @Override 207 public void onStart() { 208 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 209 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStart"); 210 } 211 super.onStart(); 212 mStarted = true; 213 214 // If the loaded account is ready now, load the UI 215 if (mAccount != null && !mLoaded) { 216 loadSettings(); 217 } 218 } 219 220 /** 221 * Called when the fragment is visible to the user and actively running. 222 * TODO: Don't read account data on UI thread. This should be fixed by removing the need 223 * to do this, not by spinning up yet another thread. 224 */ 225 @Override 226 public void onResume() { 227 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 228 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onResume"); 229 } 230 super.onResume(); 231 232 if (mAccountDirty) { 233 // if we are coming back from editing incoming or outgoing settings, 234 // we need to refresh them here so we don't accidentally overwrite the 235 // old values we're still holding here 236 mAccount.mHostAuthRecv = 237 HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv); 238 mAccount.mHostAuthSend = 239 HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeySend); 240 // Because "delete policy" UI is on edit incoming settings, we have 241 // to refresh that as well. 242 Account refreshedAccount = Account.restoreAccountWithId(mContext, mAccount.mId); 243 if (refreshedAccount == null || mAccount.mHostAuthRecv == null 244 || mAccount.mHostAuthSend == null) { 245 mSaveOnExit = false; 246 mCallback.abandonEdit(); 247 return; 248 } 249 mAccount.setDeletePolicy(refreshedAccount.getDeletePolicy()); 250 mAccountDirty = false; 251 } 252 } 253 254 @Override 255 public void onPause() { 256 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 257 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onPause"); 258 } 259 super.onPause(); 260 if (mSaveOnExit) { 261 saveSettings(); 262 } 263 } 264 265 /** 266 * Called when the Fragment is no longer started. 267 */ 268 @Override 269 public void onStop() { 270 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 271 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStop"); 272 } 273 super.onStop(); 274 mStarted = false; 275 } 276 277 /** 278 * Listen to all preference changes in this class. 279 * @param preference 280 * @param newValue 281 * @return 282 */ 283 @Override 284 public boolean onPreferenceChange(Preference preference, Object newValue){ 285 // Can't use a switch here. Falling back to a giant conditional. 286 final String key = preference.getKey(); 287 if (key.equals(PREFERENCE_DESCRIPTION)){ 288 String summary = newValue.toString().trim(); 289 if (TextUtils.isEmpty(summary)) { 290 summary = mAccount.mEmailAddress; 291 } 292 mAccountDescription.setSummary(summary); 293 mAccountDescription.setText(summary); 294 preferenceChanged(PREFERENCE_DESCRIPTION, summary); 295 return false; 296 } else if (key.equals(PREFERENCE_FREQUENCY)) { 297 final String summary = newValue.toString(); 298 final int index = mCheckFrequency.findIndexOfValue(summary); 299 mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]); 300 mCheckFrequency.setValue(summary); 301 preferenceChanged(PREFERENCE_FREQUENCY, newValue); 302 return false; 303 } else if (key.equals(PREFERENCE_SIGNATURE)) { 304 // Clean up signature if it's only whitespace (which is easy to do on a 305 // soft keyboard) but leave whitespace in place otherwise, to give the user 306 // maximum flexibility, e.g. the ability to indent 307 String signature = newValue.toString(); 308 if (signature.trim().isEmpty()) { 309 signature = ""; 310 } 311 mAccountSignature.setText(signature); 312 preferenceChanged(PREFERENCE_SIGNATURE, signature); 313 return false; 314 } else if (key.equals(PREFERENCE_NAME)) { 315 final String summary = newValue.toString().trim(); 316 if (!TextUtils.isEmpty(summary)) { 317 mAccountName.setSummary(summary); 318 mAccountName.setText(summary); 319 preferenceChanged(PREFERENCE_NAME, summary); 320 } 321 return false; 322 } else if (key.equals(PREFERENCE_VIBRATE_WHEN)) { 323 final String vibrateSetting = newValue.toString(); 324 final int index = mAccountVibrateWhen.findIndexOfValue(vibrateSetting); 325 mAccountVibrateWhen.setSummary(mAccountVibrateWhen.getEntries()[index]); 326 mAccountVibrateWhen.setValue(vibrateSetting); 327 preferenceChanged(PREFERENCE_VIBRATE_WHEN, newValue); 328 return false; 329 } else { 330 // Default behavior, just indicate that the preferences were written 331 preferenceChanged(key, newValue); 332 return true; 333 } 334 } 335 336 /** 337 * Called when the fragment is no longer in use. 338 */ 339 @Override 340 public void onDestroy() { 341 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 342 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy"); 343 } 344 super.onDestroy(); 345 346 Utility.cancelTaskInterrupt(mLoadAccountTask); 347 mLoadAccountTask = null; 348 } 349 350 @Override 351 public void onSaveInstanceState(Bundle outState) { 352 if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) { 353 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState"); 354 } 355 super.onSaveInstanceState(outState); 356 } 357 358 /** 359 * Activity provides callbacks here 360 */ 361 public void setCallback(Callback callback) { 362 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 363 } 364 365 /** 366 * Start loading a single account in preparation for editing it 367 */ 368 public void startLoadingAccount(long accountId) { 369 Utility.cancelTaskInterrupt(mLoadAccountTask); 370 mLoadAccountTask = new LoadAccountTask().executeOnExecutor( 371 AsyncTask.THREAD_POOL_EXECUTOR, accountId); 372 } 373 374 /** 375 * Async task to load account in order to view/edit it 376 */ 377 private class LoadAccountTask extends AsyncTask<Long, Void, Object[]> { 378 @Override 379 protected Object[] doInBackground(Long... params) { 380 long accountId = params[0]; 381 Account account = Account.restoreAccountWithId(mContext, accountId); 382 if (account != null) { 383 account.mHostAuthRecv = 384 HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv); 385 account.mHostAuthSend = 386 HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend); 387 if (account.mHostAuthRecv == null || account.mHostAuthSend == null) { 388 account = null; 389 } 390 } 391 long defaultAccountId = Account.getDefaultAccountId(mContext); 392 return new Object[] { account, Long.valueOf(defaultAccountId) }; 393 } 394 395 @Override 396 protected void onPostExecute(Object[] results) { 397 if (results != null && !isCancelled()) { 398 Account account = (Account) results[0]; 399 if (account == null) { 400 mSaveOnExit = false; 401 mCallback.abandonEdit(); 402 } else { 403 mAccount = account; 404 mDefaultAccountId = (Long) results[1]; 405 if (mStarted && !mLoaded) { 406 loadSettings(); 407 } 408 } 409 } 410 } 411 } 412 413 /** 414 * From a Policy, create and return an ArrayList of Strings that describe (simply) those 415 * policies that are supported by the OS. At the moment, the strings are simple (e.g. 416 * "password required"); we should probably add more information (# characters, etc.), though 417 */ 418 private ArrayList<String> getSystemPoliciesList(Policy policy) { 419 Resources res = mContext.getResources(); 420 ArrayList<String> policies = new ArrayList<String>(); 421 if (policy.mPasswordMode != Policy.PASSWORD_MODE_NONE) { 422 policies.add(res.getString(R.string.policy_require_password)); 423 } 424 if (policy.mPasswordHistory > 0) { 425 policies.add(res.getString(R.string.policy_password_history)); 426 } 427 if (policy.mPasswordExpirationDays > 0) { 428 policies.add(res.getString(R.string.policy_password_expiration)); 429 } 430 if (policy.mMaxScreenLockTime > 0) { 431 policies.add(res.getString(R.string.policy_screen_timeout)); 432 } 433 if (policy.mDontAllowCamera) { 434 policies.add(res.getString(R.string.policy_dont_allow_camera)); 435 } 436 if (policy.mMaxEmailLookback != 0) { 437 policies.add(res.getString(R.string.policy_email_age)); 438 } 439 if (policy.mMaxCalendarLookback != 0) { 440 policies.add(res.getString(R.string.policy_calendar_age)); 441 } 442 return policies; 443 } 444 445 private void setPolicyListSummary(ArrayList<String> policies, String policiesToAdd, 446 String preferenceName) { 447 Policy.addPolicyStringToList(policiesToAdd, policies); 448 if (policies.size() > 0) { 449 Preference p = findPreference(preferenceName); 450 StringBuilder sb = new StringBuilder(); 451 for (String desc: policies) { 452 sb.append(desc); 453 sb.append('\n'); 454 } 455 p.setSummary(sb.toString()); 456 } 457 } 458 459 /** 460 * Load account data into preference UI 461 */ 462 private void loadSettings() { 463 // We can only do this once, so prevent repeat 464 mLoaded = true; 465 // Once loaded the data is ready to be saved, as well 466 mSaveOnExit = false; 467 468 mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION); 469 mAccountDescription.setSummary(mAccount.getDisplayName()); 470 mAccountDescription.setText(mAccount.getDisplayName()); 471 mAccountDescription.setOnPreferenceChangeListener(this); 472 473 mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME); 474 String senderName = mAccount.getSenderName(); 475 // In rare cases, sendername will be null; Change this to empty string to avoid NPE's 476 if (senderName == null) senderName = ""; 477 mAccountName.setSummary(senderName); 478 mAccountName.setText(senderName); 479 mAccountName.setOnPreferenceChangeListener(this); 480 481 mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE); 482 mAccountSignature.setText(mAccount.getSignature()); 483 mAccountSignature.setOnPreferenceChangeListener(this); 484 485 mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); 486 String protocol = Account.getProtocol(mContext, mAccount.mId); 487 EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext, protocol); 488 mCheckFrequency.setEntries(info.syncIntervalStrings); 489 mCheckFrequency.setEntryValues(info.syncIntervals); 490 mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval())); 491 mCheckFrequency.setSummary(mCheckFrequency.getEntry()); 492 mCheckFrequency.setOnPreferenceChangeListener(this); 493 494 findPreference(PREFERENCE_QUICK_RESPONSES).setOnPreferenceClickListener( 495 new Preference.OnPreferenceClickListener() { 496 @Override 497 public boolean onPreferenceClick(Preference preference) { 498 mAccountDirty = true; 499 mCallback.onEditQuickResponses(mAccount); 500 return true; 501 } 502 }); 503 504 // Add check window preference 505 PreferenceCategory dataUsageCategory = 506 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_DATA_USAGE); 507 508 mSyncWindow = null; 509 if (info.offerLookback) { 510 mSyncWindow = new ListPreference(mContext); 511 mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label); 512 mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback())); 513 mSyncWindow.setSummary(mSyncWindow.getEntry()); 514 MailboxSettings.setupLookbackPreferenceOptions(mContext, mSyncWindow, mAccount); 515 516 // Must correspond to the hole in the XML file that's reserved. 517 mSyncWindow.setOrder(2); 518 mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 519 @Override 520 public boolean onPreferenceChange(Preference preference, Object newValue) { 521 final String summary = newValue.toString(); 522 int index = mSyncWindow.findIndexOfValue(summary); 523 mSyncWindow.setSummary(mSyncWindow.getEntries()[index]); 524 mSyncWindow.setValue(summary); 525 preferenceChanged(preference.getKey(), newValue); 526 return false; 527 } 528 }); 529 dataUsageCategory.addPreference(mSyncWindow); 530 } 531 532 mAccountBackgroundAttachments = (CheckBoxPreference) 533 findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS); 534 if (!info.offerAttachmentPreload) { 535 dataUsageCategory.removePreference(mAccountBackgroundAttachments); 536 } else { 537 mAccountBackgroundAttachments.setChecked( 538 0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS)); 539 mAccountBackgroundAttachments.setOnPreferenceChangeListener(this); 540 } 541 542 mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT); 543 mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId); 544 mAccountDefault.setOnPreferenceChangeListener(this); 545 546 mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY); 547 mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL)); 548 mAccountNotify.setOnPreferenceChangeListener(this); 549 550 mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE); 551 mAccountRingtone.setOnPreferenceChangeListener(this); 552 553 // The following two lines act as a workaround for the RingtonePreference 554 // which does not let us set/get the value programmatically 555 SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); 556 prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply(); 557 558 // Set the vibrator value, or hide it on devices w/o a vibrator 559 mAccountVibrateWhen = (ListPreference) findPreference(PREFERENCE_VIBRATE_WHEN); 560 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); 561 if (vibrator.hasVibrator()) { 562 // Calculate the value to set based on the choices, and set the value. 563 final boolean vibrateAlways = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_ALWAYS); 564 final boolean vibrateWhenSilent = 565 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_WHEN_SILENT); 566 final String vibrateSetting = 567 vibrateAlways ? PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS : 568 vibrateWhenSilent ? PREFERENCE_VALUE_VIBRATE_WHEN_SILENT : 569 PREFERENCE_VALUE_VIBRATE_WHEN_NEVER; 570 mAccountVibrateWhen.setValue(vibrateSetting); 571 572 // Update the summary string. 573 final int index = mAccountVibrateWhen.findIndexOfValue(vibrateSetting); 574 mAccountVibrateWhen.setSummary(mAccountVibrateWhen.getEntries()[index]); 575 576 // When the value is changed, update the summary in addition to the setting. 577 mAccountVibrateWhen.setOnPreferenceChangeListener(this); 578 } else { 579 // No vibrator present. Remove the preference altogether. 580 PreferenceCategory notificationsCategory = (PreferenceCategory) 581 findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS); 582 notificationsCategory.removePreference(mAccountVibrateWhen); 583 } 584 585 final Preference retryAccount = findPreference(PREFERENCE_POLICIES_RETRY_ACCOUNT); 586 final PreferenceCategory policiesCategory = (PreferenceCategory) findPreference( 587 PREFERENCE_CATEGORY_POLICIES); 588 if (mAccount.mPolicyKey > 0) { 589 // Make sure we have most recent data from account 590 mAccount.refresh(mContext); 591 Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 592 if (policy == null) { 593 // The account has been deleted? Crazy, but not impossible 594 return; 595 } 596 if (policy.mProtocolPoliciesEnforced != null) { 597 ArrayList<String> policies = getSystemPoliciesList(policy); 598 setPolicyListSummary(policies, policy.mProtocolPoliciesEnforced, 599 PREFERENCE_POLICIES_ENFORCED); 600 } 601 if (policy.mProtocolPoliciesUnsupported != null) { 602 ArrayList<String> policies = new ArrayList<String>(); 603 setPolicyListSummary(policies, policy.mProtocolPoliciesUnsupported, 604 PREFERENCE_POLICIES_UNSUPPORTED); 605 } else { 606 // Don't show "retry" unless we have unsupported policies 607 policiesCategory.removePreference(retryAccount); 608 } 609 } else { 610 // Remove the category completely if there are no policies 611 getPreferenceScreen().removePreference(policiesCategory); 612 } 613 614 retryAccount.setOnPreferenceClickListener( 615 new Preference.OnPreferenceClickListener() { 616 @Override 617 public boolean onPreferenceClick(Preference preference) { 618 // Release the account 619 SecurityPolicy.setAccountHoldFlag(mContext, mAccount, false); 620 // Remove the preference 621 policiesCategory.removePreference(retryAccount); 622 return true; 623 } 624 }); 625 findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener( 626 new Preference.OnPreferenceClickListener() { 627 @Override 628 public boolean onPreferenceClick(Preference preference) { 629 mAccountDirty = true; 630 mCallback.onIncomingSettings(mAccount); 631 return true; 632 } 633 }); 634 635 // Hide the outgoing account setup link if it's not activated 636 Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING); 637 if (info.usesSmtp) { 638 prefOutgoing.setOnPreferenceClickListener( 639 new Preference.OnPreferenceClickListener() { 640 @Override 641 public boolean onPreferenceClick(Preference preference) { 642 mAccountDirty = true; 643 mCallback.onOutgoingSettings(mAccount); 644 return true; 645 } 646 }); 647 } else { 648 PreferenceCategory serverCategory = (PreferenceCategory) findPreference( 649 PREFERENCE_CATEGORY_SERVER); 650 serverCategory.removePreference(prefOutgoing); 651 } 652 653 mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS); 654 mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR); 655 mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL); 656 if (info.syncContacts || info.syncCalendar) { 657 android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress, 658 info.accountType); 659 if (info.syncContacts) { 660 mSyncContacts.setChecked(ContentResolver 661 .getSyncAutomatically(acct, ContactsContract.AUTHORITY)); 662 mSyncContacts.setOnPreferenceChangeListener(this); 663 } else { 664 mSyncContacts.setChecked(false); 665 mSyncContacts.setEnabled(false); 666 } 667 if (info.syncCalendar) { 668 mSyncCalendar.setChecked(ContentResolver 669 .getSyncAutomatically(acct, CalendarContract.AUTHORITY)); 670 mSyncCalendar.setOnPreferenceChangeListener(this); 671 } else { 672 mSyncCalendar.setChecked(false); 673 mSyncCalendar.setEnabled(false); 674 } 675 mSyncEmail.setChecked(ContentResolver 676 .getSyncAutomatically(acct, EmailContent.AUTHORITY)); 677 mSyncEmail.setOnPreferenceChangeListener(this); 678 } else { 679 dataUsageCategory.removePreference(mSyncContacts); 680 dataUsageCategory.removePreference(mSyncCalendar); 681 dataUsageCategory.removePreference(mSyncEmail); 682 } 683 } 684 685 /** 686 * Called any time a preference is changed. 687 */ 688 private void preferenceChanged(String preference, Object value) { 689 mCallback.onSettingsChanged(mAccount, preference, value); 690 mSaveOnExit = true; 691 } 692 693 /* 694 * Note: This writes the settings on the UI thread. This has to be done so the settings are 695 * committed before we might be killed. 696 */ 697 private void saveSettings() { 698 // Turn off all controlled flags - will turn them back on while checking UI elements 699 int newFlags = mAccount.getFlags() & 700 ~(Account.FLAGS_NOTIFY_NEW_MAIL | 701 Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_VIBRATE_WHEN_SILENT | 702 Account.FLAGS_BACKGROUND_ATTACHMENTS); 703 704 newFlags |= mAccountBackgroundAttachments.isChecked() ? 705 Account.FLAGS_BACKGROUND_ATTACHMENTS : 0; 706 mAccount.setDefaultAccount(mAccountDefault.isChecked()); 707 // If the display name has been cleared, we'll reset it to the default value (email addr) 708 mAccount.setDisplayName(mAccountDescription.getText().trim()); 709 // The sender name must never be empty (this is enforced by the preference editor) 710 mAccount.setSenderName(mAccountName.getText().trim()); 711 mAccount.setSignature(mAccountSignature.getText()); 712 newFlags |= mAccountNotify.isChecked() ? Account.FLAGS_NOTIFY_NEW_MAIL : 0; 713 mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue())); 714 if (mSyncWindow != null) { 715 mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue())); 716 } 717 if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS)) { 718 newFlags |= Account.FLAGS_VIBRATE_ALWAYS; 719 } else if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_SILENT)) { 720 newFlags |= Account.FLAGS_VIBRATE_WHEN_SILENT; 721 } 722 SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); 723 mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null)); 724 mAccount.setFlags(newFlags); 725 726 EmailServiceInfo info = 727 EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext)); 728 if (info.syncContacts || info.syncCalendar) { 729 android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress, 730 AccountManagerTypes.TYPE_EXCHANGE); 731 ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY, 732 mSyncContacts.isChecked()); 733 ContentResolver.setSyncAutomatically(acct, CalendarContract.AUTHORITY, 734 mSyncCalendar.isChecked()); 735 ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY, 736 mSyncEmail.isChecked()); 737 } 738 739 // Commit the changes 740 // Note, this is done in the UI thread because at this point, we must commit 741 // all changes - any time after onPause completes, we could be killed. This is analogous 742 // to the way that SharedPreferences tries to work off-thread in apply(), but will pause 743 // until completion in onPause(). 744 ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount); 745 mAccount.update(mContext, cv); 746 747 // Run the remaining changes off-thread 748 MailActivityEmail.setServicesEnabledAsync(mContext); 749 } 750 751 public String getAccountEmail() { 752 // Get the e-mail address of the account being editted, if this is for an existing account. 753 return mAccountEmail; 754 } 755} 756