AccountSettingsFragment.java revision 12b82d9374947c9268217f45befe8a74bd9b60d7
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 com.android.email.Email; 20import com.android.email.R; 21import com.android.email.mail.Sender; 22import com.android.email.mail.Store; 23import com.android.emailcommon.AccountManagerTypes; 24import com.android.emailcommon.CalendarProviderStub; 25import com.android.emailcommon.Logging; 26import com.android.emailcommon.mail.MessagingException; 27import com.android.emailcommon.provider.EmailContent; 28import com.android.emailcommon.provider.EmailContent.Account; 29import com.android.emailcommon.provider.HostAuth; 30import com.android.emailcommon.utility.Utility; 31 32import android.app.Activity; 33import android.app.AlertDialog; 34import android.app.Dialog; 35import android.app.DialogFragment; 36import android.app.Fragment; 37import android.app.FragmentTransaction; 38import android.content.ContentResolver; 39import android.content.ContentValues; 40import android.content.Context; 41import android.content.DialogInterface; 42import android.content.SharedPreferences; 43import android.os.AsyncTask; 44import android.os.Bundle; 45import android.os.Vibrator; 46import android.preference.CheckBoxPreference; 47import android.preference.EditTextPreference; 48import android.preference.ListPreference; 49import android.preference.Preference; 50import android.preference.PreferenceActivity; 51import android.preference.PreferenceCategory; 52import android.preference.PreferenceFragment; 53import android.preference.RingtonePreference; 54import android.provider.ContactsContract; 55import android.text.TextUtils; 56import android.util.Log; 57 58/** 59 * Fragment containing the main logic for account settings. This also calls out to other 60 * fragments for server settings. 61 * 62 * TODO: Remove or make async the mAccountDirty reload logic. Probably no longer needed. 63 * TODO: Can we defer calling addPreferencesFromResource() until after we load the account? This 64 * could reduce flicker. 65 */ 66public class AccountSettingsFragment extends PreferenceFragment { 67 68 // Keys used for arguments bundle 69 private static final String BUNDLE_KEY_ACCOUNT_ID = "AccountSettingsFragment.AccountId"; 70 private static final String BUNDLE_KEY_ACCOUNT_EMAIL = "AccountSettingsFragment.Email"; 71 72 private static final String PREFERENCE_CATEGORY_TOP = "account_settings"; 73 public static final String PREFERENCE_DESCRIPTION = "account_description"; 74 private static final String PREFERENCE_NAME = "account_name"; 75 private static final String PREFERENCE_SIGNATURE = "account_signature"; 76 private static final String PREFERENCE_FREQUENCY = "account_check_frequency"; 77 private static final String PREFERENCE_BACKGROUND_ATTACHMENTS = 78 "account_background_attachments"; 79 private static final String PREFERENCE_DEFAULT = "account_default"; 80 private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "account_notifications"; 81 private static final String PREFERENCE_NOTIFY = "account_notify"; 82 private static final String PREFERENCE_VIBRATE_WHEN = "account_settings_vibrate_when"; 83 private static final String PREFERENCE_RINGTONE = "account_ringtone"; 84 private static final String PREFERENCE_CATEGORY_SERVER = "account_servers"; 85 private static final String PREFERENCE_INCOMING = "incoming"; 86 private static final String PREFERENCE_OUTGOING = "outgoing"; 87 private static final String PREFERENCE_SYNC_CONTACTS = "account_sync_contacts"; 88 private static final String PREFERENCE_SYNC_CALENDAR = "account_sync_calendar"; 89 private static final String PREFERENCE_SYNC_EMAIL = "account_sync_email"; 90 private static final String PREFERENCE_DELETE_ACCOUNT = "delete_account"; 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 onIncomingSettings(Account account); 132 public void onOutgoingSettings(Account account); 133 public void abandonEdit(); 134 public void deleteAccount(Account account); 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 onIncomingSettings(Account account) {} 141 @Override public void onOutgoingSettings(Account account) {} 142 @Override public void abandonEdit() {} 143 @Override public void deleteAccount(Account account) {} 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 && Email.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 && Email.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 && Email.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 && Email.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 && Email.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 && Email.DEBUG) { 270 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onStop"); 271 } 272 super.onStop(); 273 mStarted = false; 274 } 275 276 /** 277 * Called when the fragment is no longer in use. 278 */ 279 @Override 280 public void onDestroy() { 281 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 282 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onDestroy"); 283 } 284 super.onDestroy(); 285 286 Utility.cancelTaskInterrupt(mLoadAccountTask); 287 mLoadAccountTask = null; 288 } 289 290 @Override 291 public void onSaveInstanceState(Bundle outState) { 292 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 293 Log.d(Logging.LOG_TAG, "AccountSettingsFragment onSaveInstanceState"); 294 } 295 super.onSaveInstanceState(outState); 296 } 297 298 /** 299 * Activity provides callbacks here 300 */ 301 public void setCallback(Callback callback) { 302 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 303 } 304 305 /** 306 * Start loading a single account in preparation for editing it 307 */ 308 public void startLoadingAccount(long accountId) { 309 Utility.cancelTaskInterrupt(mLoadAccountTask); 310 mLoadAccountTask = new LoadAccountTask().execute(accountId); 311 } 312 313 /** 314 * Async task to load account in order to view/edit it 315 */ 316 private class LoadAccountTask extends AsyncTask<Long, Void, Object[]> { 317 @Override 318 protected Object[] doInBackground(Long... params) { 319 long accountId = params[0]; 320 Account account = Account.restoreAccountWithId(mContext, accountId); 321 if (account != null) { 322 account.mHostAuthRecv = 323 HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeyRecv); 324 account.mHostAuthSend = 325 HostAuth.restoreHostAuthWithId(mContext, account.mHostAuthKeySend); 326 if (account.mHostAuthRecv == null || account.mHostAuthSend == null) { 327 account = null; 328 } 329 } 330 long defaultAccountId = Account.getDefaultAccountId(mContext); 331 return new Object[] { account, Long.valueOf(defaultAccountId) }; 332 } 333 334 @Override 335 protected void onPostExecute(Object[] results) { 336 if (results != null && !isCancelled()) { 337 Account account = (Account) results[0]; 338 if (account == null) { 339 mSaveOnExit = false; 340 mCallback.abandonEdit(); 341 } else { 342 mAccount = account; 343 mDefaultAccountId = (Long) results[1]; 344 if (mStarted && !mLoaded) { 345 loadSettings(); 346 } 347 } 348 } 349 } 350 } 351 352 /** 353 * Load account data into preference UI 354 */ 355 private void loadSettings() { 356 // We can only do this once, so prevent repeat 357 mLoaded = true; 358 // Once loaded the data is ready to be saved, as well 359 mSaveOnExit = false; 360 361 PreferenceCategory topCategory = 362 (PreferenceCategory) findPreference(PREFERENCE_CATEGORY_TOP); 363 topCategory.setTitle(mContext.getString(R.string.account_settings_title_fmt)); 364 365 mAccountDescription = (EditTextPreference) findPreference(PREFERENCE_DESCRIPTION); 366 mAccountDescription.setSummary(mAccount.getDisplayName()); 367 mAccountDescription.setText(mAccount.getDisplayName()); 368 mAccountDescription.setOnPreferenceChangeListener( 369 new Preference.OnPreferenceChangeListener() { 370 public boolean onPreferenceChange(Preference preference, Object newValue) { 371 String summary = newValue.toString().trim(); 372 if (TextUtils.isEmpty(summary)) { 373 summary = mAccount.mEmailAddress; 374 } 375 mAccountDescription.setSummary(summary); 376 mAccountDescription.setText(summary); 377 onPreferenceChanged(PREFERENCE_DESCRIPTION, summary); 378 return false; 379 } 380 } 381 ); 382 383 mAccountName = (EditTextPreference) findPreference(PREFERENCE_NAME); 384 String senderName = mAccount.getSenderName(); 385 // In rare cases, sendername will be null; Change this to empty string to avoid NPE's 386 if (senderName == null) senderName = ""; 387 mAccountName.setSummary(senderName); 388 mAccountName.setText(senderName); 389 mAccountName.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 390 public boolean onPreferenceChange(Preference preference, Object newValue) { 391 final String summary = newValue.toString().trim(); 392 if (!TextUtils.isEmpty(summary)) { 393 mAccountName.setSummary(summary); 394 mAccountName.setText(summary); 395 onPreferenceChanged(PREFERENCE_NAME, summary); 396 } 397 return false; 398 } 399 }); 400 401 mAccountSignature = (EditTextPreference) findPreference(PREFERENCE_SIGNATURE); 402 String signature = mAccount.getSignature(); 403 mAccountSignature.setText(mAccount.getSignature()); 404 mAccountSignature.setOnPreferenceChangeListener( 405 new Preference.OnPreferenceChangeListener() { 406 public boolean onPreferenceChange(Preference preference, Object newValue) { 407 // Clean up signature if it's only whitespace (which is easy to do on a 408 // soft keyboard) but leave whitespace in place otherwise, to give the user 409 // maximum flexibility, e.g. the ability to indent 410 String signature = newValue.toString(); 411 if (signature.trim().isEmpty()) { 412 signature = ""; 413 } 414 mAccountSignature.setText(signature); 415 onPreferenceChanged(PREFERENCE_SIGNATURE, signature); 416 return false; 417 } 418 }); 419 420 mCheckFrequency = (ListPreference) findPreference(PREFERENCE_FREQUENCY); 421 422 // Before setting value, we may need to adjust the lists 423 Store.StoreInfo info = Store.StoreInfo.getStoreInfo(mAccount.getStoreUri(mContext), 424 mContext); 425 if (info.mPushSupported) { 426 mCheckFrequency.setEntries(R.array.account_settings_check_frequency_entries_push); 427 mCheckFrequency.setEntryValues(R.array.account_settings_check_frequency_values_push); 428 } 429 430 mCheckFrequency.setValue(String.valueOf(mAccount.getSyncInterval())); 431 mCheckFrequency.setSummary(mCheckFrequency.getEntry()); 432 mCheckFrequency.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 433 public boolean onPreferenceChange(Preference preference, Object newValue) { 434 final String summary = newValue.toString(); 435 int index = mCheckFrequency.findIndexOfValue(summary); 436 mCheckFrequency.setSummary(mCheckFrequency.getEntries()[index]); 437 mCheckFrequency.setValue(summary); 438 onPreferenceChanged(PREFERENCE_FREQUENCY, newValue); 439 return false; 440 } 441 }); 442 443 // Add check window preference 444 mSyncWindow = null; 445 if (info.mVisibleLimitDefault == -1) { 446 mSyncWindow = new ListPreference(mContext); 447 mSyncWindow.setTitle(R.string.account_setup_options_mail_window_label); 448 mSyncWindow.setEntries(R.array.account_settings_mail_window_entries); 449 mSyncWindow.setEntryValues(R.array.account_settings_mail_window_values); 450 mSyncWindow.setValue(String.valueOf(mAccount.getSyncLookback())); 451 mSyncWindow.setSummary(mSyncWindow.getEntry()); 452 mSyncWindow.setOrder(4); 453 mSyncWindow.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 454 public boolean onPreferenceChange(Preference preference, Object newValue) { 455 final String summary = newValue.toString(); 456 int index = mSyncWindow.findIndexOfValue(summary); 457 mSyncWindow.setSummary(mSyncWindow.getEntries()[index]); 458 mSyncWindow.setValue(summary); 459 onPreferenceChanged(preference.getKey(), newValue); 460 return false; 461 } 462 }); 463 topCategory.addPreference(mSyncWindow); 464 } 465 466 // Show "background attachments" for IMAP & EAS - hide it for POP3. 467 mAccountBackgroundAttachments = (CheckBoxPreference) 468 findPreference(PREFERENCE_BACKGROUND_ATTACHMENTS); 469 if ("pop3".equals(mAccount.mHostAuthRecv.mProtocol)) { 470 topCategory.removePreference(mAccountBackgroundAttachments); 471 } else { 472 mAccountBackgroundAttachments.setChecked( 473 0 != (mAccount.getFlags() & Account.FLAGS_BACKGROUND_ATTACHMENTS)); 474 mAccountBackgroundAttachments.setOnPreferenceChangeListener(mPreferenceChangeListener); 475 } 476 477 mAccountDefault = (CheckBoxPreference) findPreference(PREFERENCE_DEFAULT); 478 mAccountDefault.setChecked(mAccount.mId == mDefaultAccountId); 479 mAccountDefault.setOnPreferenceChangeListener(mPreferenceChangeListener); 480 481 mAccountNotify = (CheckBoxPreference) findPreference(PREFERENCE_NOTIFY); 482 mAccountNotify.setChecked(0 != (mAccount.getFlags() & Account.FLAGS_NOTIFY_NEW_MAIL)); 483 mAccountNotify.setOnPreferenceChangeListener(mPreferenceChangeListener); 484 485 mAccountRingtone = (RingtonePreference) findPreference(PREFERENCE_RINGTONE); 486 mAccountRingtone.setOnPreferenceChangeListener(mPreferenceChangeListener); 487 488 // The following two lines act as a workaround for the RingtonePreference 489 // which does not let us set/get the value programmatically 490 SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); 491 prefs.edit().putString(PREFERENCE_RINGTONE, mAccount.getRingtone()).apply(); 492 493 // Set the vibrator value, or hide it on devices w/o a vibrator 494 mAccountVibrateWhen = (ListPreference) findPreference(PREFERENCE_VIBRATE_WHEN); 495 Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); 496 if (vibrator.hasVibrator()) { 497 boolean flagsVibrate = 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_ALWAYS); 498 boolean flagsVibrateSilent = 499 0 != (mAccount.getFlags() & Account.FLAGS_VIBRATE_WHEN_SILENT); 500 mAccountVibrateWhen.setValue( 501 flagsVibrate ? PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS : 502 flagsVibrateSilent ? PREFERENCE_VALUE_VIBRATE_WHEN_SILENT : 503 PREFERENCE_VALUE_VIBRATE_WHEN_NEVER); 504 mAccountVibrateWhen.setOnPreferenceChangeListener(mPreferenceChangeListener); 505 } else { 506 PreferenceCategory notificationsCategory = (PreferenceCategory) 507 findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS); 508 notificationsCategory.removePreference(mAccountVibrateWhen); 509 } 510 511 findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener( 512 new Preference.OnPreferenceClickListener() { 513 public boolean onPreferenceClick(Preference preference) { 514 mAccountDirty = true; 515 mCallback.onIncomingSettings(mAccount); 516 return true; 517 } 518 }); 519 520 // Hide the outgoing account setup link if it's not activated 521 Preference prefOutgoing = findPreference(PREFERENCE_OUTGOING); 522 boolean showOutgoing = true; 523 try { 524 Sender sender = Sender.getInstance(mContext, mAccount); 525 if (sender != null) { 526 Class<? extends android.app.Activity> setting = sender.getSettingActivityClass(); 527 showOutgoing = (setting != null); 528 } 529 } catch (MessagingException me) { 530 // just leave showOutgoing as true - bias towards showing it, so user can fix it 531 } 532 if (showOutgoing) { 533 prefOutgoing.setOnPreferenceClickListener( 534 new Preference.OnPreferenceClickListener() { 535 public boolean onPreferenceClick(Preference preference) { 536 mAccountDirty = true; 537 mCallback.onOutgoingSettings(mAccount); 538 return true; 539 } 540 }); 541 } else { 542 PreferenceCategory serverCategory = (PreferenceCategory) findPreference( 543 PREFERENCE_CATEGORY_SERVER); 544 serverCategory.removePreference(prefOutgoing); 545 } 546 547 mSyncContacts = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CONTACTS); 548 mSyncCalendar = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_CALENDAR); 549 mSyncEmail = (CheckBoxPreference) findPreference(PREFERENCE_SYNC_EMAIL); 550 if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) { 551 android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress, 552 AccountManagerTypes.TYPE_EXCHANGE); 553 mSyncContacts.setChecked(ContentResolver 554 .getSyncAutomatically(acct, ContactsContract.AUTHORITY)); 555 mSyncContacts.setOnPreferenceChangeListener(mPreferenceChangeListener); 556 mSyncCalendar.setChecked(ContentResolver 557 .getSyncAutomatically(acct, CalendarProviderStub.AUTHORITY)); 558 mSyncCalendar.setOnPreferenceChangeListener(mPreferenceChangeListener); 559 mSyncEmail.setChecked(ContentResolver 560 .getSyncAutomatically(acct, EmailContent.AUTHORITY)); 561 mSyncEmail.setOnPreferenceChangeListener(mPreferenceChangeListener); 562 } else { 563 PreferenceCategory serverCategory = (PreferenceCategory) findPreference( 564 PREFERENCE_CATEGORY_SERVER); 565 serverCategory.removePreference(mSyncContacts); 566 serverCategory.removePreference(mSyncCalendar); 567 serverCategory.removePreference(mSyncEmail); 568 } 569 570 // Temporary home for delete account 571 Preference prefDeleteAccount = findPreference(PREFERENCE_DELETE_ACCOUNT); 572 prefDeleteAccount.setOnPreferenceClickListener( 573 new Preference.OnPreferenceClickListener() { 574 public boolean onPreferenceClick(Preference preference) { 575 DeleteAccountFragment dialogFragment = DeleteAccountFragment.newInstance( 576 mAccount, AccountSettingsFragment.this); 577 FragmentTransaction ft = getFragmentManager().beginTransaction(); 578 ft.addToBackStack(null); 579 dialogFragment.show(ft, DeleteAccountFragment.TAG); 580 return true; 581 } 582 }); 583 } 584 585 /** 586 * Generic onPreferenceChanged listener for the preferences (above) that just need 587 * to be written, without extra tweaks 588 */ 589 private final Preference.OnPreferenceChangeListener mPreferenceChangeListener = 590 new Preference.OnPreferenceChangeListener() { 591 public boolean onPreferenceChange(Preference preference, Object newValue) { 592 onPreferenceChanged(preference.getKey(), newValue); 593 return true; 594 } 595 }; 596 597 /** 598 * Called any time a preference is changed. 599 */ 600 private void onPreferenceChanged(String preference, Object value) { 601 mCallback.onSettingsChanged(mAccount, preference, value); 602 mSaveOnExit = true; 603 } 604 605 /* 606 * Note: This writes the settings on the UI thread. This has to be done so the settings are 607 * committed before we might be killed. 608 */ 609 private void saveSettings() { 610 // Turn off all controlled flags - will turn them back on while checking UI elements 611 int newFlags = mAccount.getFlags() & 612 ~(Account.FLAGS_NOTIFY_NEW_MAIL | 613 Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_VIBRATE_WHEN_SILENT | 614 Account.FLAGS_BACKGROUND_ATTACHMENTS); 615 616 newFlags |= mAccountBackgroundAttachments.isChecked() ? 617 Account.FLAGS_BACKGROUND_ATTACHMENTS : 0; 618 mAccount.setDefaultAccount(mAccountDefault.isChecked()); 619 // If the display name has been cleared, we'll reset it to the default value (email addr) 620 mAccount.setDisplayName(mAccountDescription.getText().trim()); 621 // The sender name must never be empty (this is enforced by the preference editor) 622 mAccount.setSenderName(mAccountName.getText().trim()); 623 mAccount.setSignature(mAccountSignature.getText()); 624 newFlags |= mAccountNotify.isChecked() ? Account.FLAGS_NOTIFY_NEW_MAIL : 0; 625 mAccount.setSyncInterval(Integer.parseInt(mCheckFrequency.getValue())); 626 if (mSyncWindow != null) { 627 mAccount.setSyncLookback(Integer.parseInt(mSyncWindow.getValue())); 628 } 629 if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_ALWAYS)) { 630 newFlags |= Account.FLAGS_VIBRATE_ALWAYS; 631 } else if (mAccountVibrateWhen.getValue().equals(PREFERENCE_VALUE_VIBRATE_WHEN_SILENT)) { 632 newFlags |= Account.FLAGS_VIBRATE_WHEN_SILENT; 633 } 634 SharedPreferences prefs = mAccountRingtone.getPreferenceManager().getSharedPreferences(); 635 mAccount.setRingtone(prefs.getString(PREFERENCE_RINGTONE, null)); 636 mAccount.setFlags(newFlags); 637 638 if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) { 639 android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress, 640 AccountManagerTypes.TYPE_EXCHANGE); 641 ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY, 642 mSyncContacts.isChecked()); 643 ContentResolver.setSyncAutomatically(acct, CalendarProviderStub.AUTHORITY, 644 mSyncCalendar.isChecked()); 645 ContentResolver.setSyncAutomatically(acct, EmailContent.AUTHORITY, 646 mSyncEmail.isChecked()); 647 } 648 649 // Commit the changes 650 // Note, this is done in the UI thread because at this point, we must commit 651 // all changes - any time after onPause completes, we could be killed. This is analogous 652 // to the way that SharedPreferences tries to work off-thread in apply(), but will pause 653 // until completion in onPause(). 654 ContentValues cv = AccountSettingsUtils.getAccountContentValues(mAccount); 655 mAccount.update(mContext, cv); 656 657 // Run the remaining changes off-thread 658 Email.setServicesEnabledAsync(mContext); 659 } 660 661 /** 662 * Dialog fragment to show "remove account?" dialog 663 */ 664 public static class DeleteAccountFragment extends DialogFragment { 665 private final static String TAG = "DeleteAccountFragment"; 666 667 // Argument bundle keys 668 private final static String BUNDLE_KEY_ACCOUNT_NAME = "DeleteAccountFragment.Name"; 669 670 /** 671 * Create the dialog with parameters 672 */ 673 public static DeleteAccountFragment newInstance(Account account, Fragment parentFragment) { 674 DeleteAccountFragment f = new DeleteAccountFragment(); 675 Bundle b = new Bundle(); 676 b.putString(BUNDLE_KEY_ACCOUNT_NAME, account.getDisplayName()); 677 f.setArguments(b); 678 f.setTargetFragment(parentFragment, 0); 679 return f; 680 } 681 682 @Override 683 public Dialog onCreateDialog(Bundle savedInstanceState) { 684 Context context = getActivity(); 685 final String name = getArguments().getString(BUNDLE_KEY_ACCOUNT_NAME); 686 687 return new AlertDialog.Builder(context) 688 .setIconAttribute(android.R.attr.alertDialogIcon) 689 .setTitle(R.string.account_delete_dlg_title) 690 .setMessage(context.getString(R.string.account_delete_dlg_instructions_fmt, name)) 691 .setPositiveButton( 692 R.string.okay_action, 693 new DialogInterface.OnClickListener() { 694 public void onClick(DialogInterface dialog, int whichButton) { 695 Fragment f = getTargetFragment(); 696 if (f instanceof AccountSettingsFragment) { 697 ((AccountSettingsFragment)f).finishDeleteAccount(); 698 } 699 dismiss(); 700 } 701 }) 702 .setNegativeButton( 703 R.string.cancel_action, 704 new DialogInterface.OnClickListener() { 705 public void onClick(DialogInterface dialog, int whichButton) { 706 dismiss(); 707 } 708 }) 709 .create(); 710 } 711 } 712 713 /** 714 * Callback from delete account dialog - passes the delete command up to the activity 715 */ 716 private void finishDeleteAccount() { 717 mSaveOnExit = false; 718 mCallback.deleteAccount(mAccount); 719 } 720 721 public String getAccountEmail() { 722 // Get the e-mail address of the account being editted, if this is for an existing account. 723 return mAccountEmail; 724 } 725} 726