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