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