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