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