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