MailboxSettings.java revision a49da871916edf525edf366ec754b1db60ba4796
1/* 2 * Copyright (C) 2011 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.ActionBar; 20import android.app.Activity; 21import android.content.ContentUris; 22import android.content.ContentValues; 23import android.content.Context; 24import android.content.Intent; 25import android.net.Uri; 26import android.os.Bundle; 27import android.preference.ListPreference; 28import android.preference.Preference; 29import android.preference.Preference.OnPreferenceChangeListener; 30import android.preference.PreferenceActivity; 31import android.util.Log; 32import android.view.MenuItem; 33 34import com.android.email.Email; 35import com.android.email.FolderProperties; 36import com.android.email.R; 37import com.android.email.RefreshManager; 38import com.android.emailcommon.Logging; 39import com.android.emailcommon.provider.Account; 40import com.android.emailcommon.provider.EmailContent.AccountColumns; 41import com.android.emailcommon.provider.EmailContent.MailboxColumns; 42import com.android.emailcommon.provider.Mailbox; 43import com.android.emailcommon.utility.EmailAsyncTask; 44import com.google.common.base.Objects; 45import com.google.common.base.Preconditions; 46 47/** 48 * "Mailbox settings" activity. 49 * 50 * It's used to update per-mailbox sync settings. It normally updates Mailbox settings, unless 51 * the target mailbox is Inbox, in which case it updates Account settings instead. 52 * 53 * All changes made by the user will not be immediately saved to the database, as changing the 54 * sync window may result in removal of messages. Instead, we only save to the database in {@link 55 * #onDestroy()}, unless it's called for configuration changes. 56 */ 57public class MailboxSettings extends PreferenceActivity { 58 private static final String EXTRA_MAILBOX_ID = "MAILBOX_ID"; 59 private static final String BUNDLE_ACCOUNT = "MailboxSettings.account"; 60 private static final String BUNDLE_MAILBOX = "MailboxSettings.mailbox"; 61 private static final String BUNDLE_NEEDS_SAVE = "MailboxSettings.needsSave"; 62 63 private static final String PREF_CHECK_FREQUENCY_KEY = "check_frequency"; 64 private static final String PREF_SYNC_WINDOW_KEY = "sync_window"; 65 66 private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); 67 68 // Account and Mailbox -- directly loaded by LoadMailboxTask 69 private Account mAccount; 70 private Mailbox mMailbox; 71 private boolean mNeedsSave; 72 73 private ListPreference mSyncIntervalPref; 74 private ListPreference mSyncLookbackPref; 75 76 /** 77 * Starts the activity for a mailbox. 78 */ 79 public static final void start(Activity parent, long mailboxId) { 80 Intent i = new Intent(parent, MailboxSettings.class); 81 i.putExtra(EXTRA_MAILBOX_ID, mailboxId); 82 parent.startActivity(i); 83 } 84 85 @Override 86 protected void onCreate(Bundle savedInstanceState) { 87 super.onCreate(savedInstanceState); 88 89 final long mailboxId = getIntent().getLongExtra(EXTRA_MAILBOX_ID, Mailbox.NO_MAILBOX); 90 if (mailboxId == Mailbox.NO_MAILBOX) { 91 finish(); 92 return; 93 } 94 95 addPreferencesFromResource(R.xml.mailbox_preferences); 96 97 mSyncIntervalPref = (ListPreference) findPreference(PREF_CHECK_FREQUENCY_KEY); 98 mSyncLookbackPref = (ListPreference) findPreference(PREF_SYNC_WINDOW_KEY); 99 100 mSyncIntervalPref.setOnPreferenceChangeListener(mPreferenceChanged); 101 mSyncLookbackPref.setOnPreferenceChangeListener(mPreferenceChanged); 102 103 // Make them disabled until we load data 104 enablePreferences(false); 105 106 if (savedInstanceState != null) { 107 mAccount = savedInstanceState.getParcelable(BUNDLE_ACCOUNT); 108 mMailbox = savedInstanceState.getParcelable(BUNDLE_MAILBOX); 109 mNeedsSave = savedInstanceState.getBoolean(BUNDLE_NEEDS_SAVE); 110 } 111 if (mAccount == null) { 112 new LoadMailboxTask(mailboxId).executeParallel((Void[]) null); 113 } else { 114 onDataLoaded(); 115 } 116 117 // Always show "app up" as we expect our parent to be an Email activity. 118 ActionBar actionBar = getActionBar(); 119 if (actionBar != null) { 120 actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP); 121 } 122 } 123 124 private void enablePreferences(boolean enabled) { 125 mSyncIntervalPref.setEnabled(enabled); 126 mSyncLookbackPref.setEnabled(enabled); 127 } 128 129 @Override 130 protected void onSaveInstanceState(Bundle outState) { 131 super.onSaveInstanceState(outState); 132 outState.putParcelable(BUNDLE_ACCOUNT, mAccount); 133 outState.putParcelable(BUNDLE_MAILBOX, mMailbox); 134 outState.putBoolean(BUNDLE_NEEDS_SAVE, mNeedsSave); 135 } 136 137 /** 138 * We save all the settings in onDestroy, *unless it's for configuration changes*. 139 */ 140 @Override 141 protected void onDestroy() { 142 mTaskTracker.cancellAllInterrupt(); 143 if (!isChangingConfigurations()) { 144 saveToDatabase(); 145 } 146 super.onDestroy(); 147 } 148 149 /** 150 * Loads {@link #mAccount} and {@link #mMailbox}. 151 */ 152 private class LoadMailboxTask extends EmailAsyncTask<Void, Void, Void> { 153 private final long mMailboxId; 154 155 public LoadMailboxTask(long mailboxId) { 156 super(mTaskTracker); 157 mMailboxId = mailboxId; 158 } 159 160 @Override 161 protected Void doInBackground(Void... params) { 162 final Context c = MailboxSettings.this; 163 mMailbox = Mailbox.restoreMailboxWithId(c, mMailboxId); 164 if (mMailbox != null) { 165 mAccount = Account.restoreAccountWithId(c, mMailbox.mAccountKey); 166 } 167 return null; 168 } 169 170 @Override 171 protected void onSuccess(Void result) { 172 if ((mAccount == null) || (mMailbox == null)) { 173 finish(); // Account or mailbox removed. 174 return; 175 } 176 onDataLoaded(); 177 } 178 } 179 180 /** 181 * Called when {@link #mAccount} and {@link #mMailbox} are loaded (either by the async task 182 * or from the saved state). 183 */ 184 private void onDataLoaded() { 185 Preconditions.checkNotNull(mAccount); 186 Preconditions.checkNotNull(mMailbox); 187 188 // Update the title with the mailbox name. 189 ActionBar actionBar = getActionBar(); 190 String mailboxName = FolderProperties.getInstance(this).getDisplayName(mMailbox); 191 if (actionBar != null) { 192 actionBar.setTitle(mailboxName); 193 actionBar.setSubtitle(getString(R.string.mailbox_settings_activity_title)); 194 } else { 195 setTitle(getString(R.string.mailbox_settings_activity_title_with_mailbox, mailboxName)); 196 } 197 198 199 // Special case: If setting inbox, don't show "Use account's default". 200 if (mMailbox.mType == Mailbox.TYPE_INBOX) { 201 mSyncLookbackPref.setEntries(R.array.account_settings_mail_window_entries); 202 mSyncLookbackPref.setEntryValues(R.array.account_settings_mail_window_values); 203 } 204 205 // Set default value & update summary 206 mSyncIntervalPref.setValue(String.valueOf(getSyncInterval())); 207 mSyncLookbackPref.setValue(String.valueOf(getSyncLookback())); 208 209 updatePreferenceSummary(); 210 211 // Make then enabled 212 enablePreferences(true); 213 214 } 215 216 private void updatePreferenceSummary() { 217 mSyncIntervalPref.setSummary(mSyncIntervalPref.getEntry()); 218 mSyncLookbackPref.setSummary(mSyncLookbackPref.getEntry()); 219 } 220 221 /** 222 * @return current sync interval setting from the objects 223 */ 224 private int getSyncInterval() { 225 int syncInterval; 226 if (mMailbox.mType == Mailbox.TYPE_INBOX) { 227 syncInterval = mAccount.mSyncInterval; 228 } else { 229 if (mMailbox.mSyncInterval == 0) { 230 // 0 is the default value, and it means "don't sync" (for non-inbox mailboxes) 231 syncInterval = Mailbox.CHECK_INTERVAL_NEVER; 232 } else { 233 syncInterval = mMailbox.mSyncInterval; 234 } 235 } 236 // In the case of the internal push states, use "push" 237 if (syncInterval == Mailbox.CHECK_INTERVAL_PING || 238 syncInterval == Mailbox.CHECK_INTERVAL_PUSH_HOLD) { 239 syncInterval = Mailbox.CHECK_INTERVAL_PUSH; 240 } 241 return syncInterval; 242 } 243 244 /** 245 * @return current sync lookback setting from the objects 246 */ 247 private int getSyncLookback() { 248 if (mMailbox.mType == Mailbox.TYPE_INBOX) { 249 return mAccount.mSyncLookback; 250 } else { 251 // Here, 0 is valid and means "use the account default sync window". 252 return mMailbox.mSyncLookback; 253 } 254 } 255 256 private final OnPreferenceChangeListener mPreferenceChanged = new OnPreferenceChangeListener() { 257 @Override 258 public boolean onPreferenceChange(Preference preference, Object newValue) { 259 final ListPreference lp = (ListPreference) preference; 260 if (Objects.equal(lp.getValue(), newValue)) { 261 return false; 262 } 263 mNeedsSave = true; 264 if (Email.DEBUG) { 265 Log.i(Logging.LOG_TAG, "Setting changed"); 266 } 267 // In order to set the current entry to the summary, we need to udpate the value 268 // manually, rather than letting the framework do that (by returning true). 269 lp.setValue((String) newValue); 270 updatePreferenceSummary(); 271 updateObjects(); 272 return false; 273 } 274 }; 275 276 /** 277 * Updates {@link #mAccount}/{@link #mMailbox}, but doesn't save to the database yet. 278 */ 279 private void updateObjects() { 280 final int syncInterval = Integer.valueOf(mSyncIntervalPref.getValue()); 281 final int syncLookback = Integer.valueOf(mSyncLookbackPref.getValue()); 282 if (Email.DEBUG) { 283 Log.i(Logging.LOG_TAG, "Updating object: " + syncInterval + "," + syncLookback); 284 } 285 if (mMailbox.mType == Mailbox.TYPE_INBOX) { 286 mAccount.mSyncInterval = syncInterval; 287 mAccount.mSyncLookback = syncLookback; 288 } else { 289 mMailbox.mSyncInterval = syncInterval; 290 mMailbox.mSyncLookback = syncLookback; 291 } 292 } 293 294 /** 295 * Save changes to the database. 296 * 297 * Note it's called from {@link #onDestroy()}, which is called on the UI thread where we're not 298 * allowed to touch the database, so it uses {@link EmailAsyncTask} to do the save on a bg 299 * thread. This unfortunately means there's a chance that the app gets killed before the save is 300 * finished. 301 */ 302 private void saveToDatabase() { 303 if (!mNeedsSave) { 304 return; 305 } 306 Log.i(Logging.LOG_TAG, "Saving mailbox settings..."); 307 enablePreferences(false); 308 309 // Since the activity will be destroyed... 310 // Create local references (Although it's really okay to touch members of a destroyed 311 // activity...) 312 final Account account = mAccount; 313 final Mailbox mailbox = mMailbox; 314 final Context context = getApplicationContext(); 315 316 new EmailAsyncTask<Void, Void, Void> (null /* no cancel */) { 317 @Override 318 protected Void doInBackground(Void... params) { 319 final ContentValues cv = new ContentValues(); 320 final Uri uri; 321 322 if (mailbox.mType == Mailbox.TYPE_INBOX) { 323 cv.put(AccountColumns.SYNC_INTERVAL, account.mSyncInterval); 324 cv.put(AccountColumns.SYNC_LOOKBACK, account.mSyncLookback); 325 uri = ContentUris.withAppendedId(Account.CONTENT_URI, account.mId); 326 } else { 327 cv.put(MailboxColumns.SYNC_INTERVAL, mailbox.mSyncInterval); 328 cv.put(MailboxColumns.SYNC_LOOKBACK, mailbox.mSyncLookback); 329 uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailbox.mId); 330 } 331 context.getContentResolver().update(uri, cv, null, null); 332 333 Log.i(Logging.LOG_TAG, "Saved: " + uri); 334 return null; 335 } 336 337 @Override 338 protected void onSuccess(Void result) { 339 // must be called on the ui thread 340 RefreshManager.getInstance(context).refreshMessageList(account.mId, mailbox.mId, 341 true); 342 } 343 }.executeSerial((Void [])null); 344 } 345 346 @Override 347 public boolean onOptionsItemSelected(MenuItem item) { 348 if (item.getItemId() == android.R.id.home) { 349 onBackPressed(); 350 return true; 351 } 352 return super.onOptionsItemSelected(item); 353 } 354} 355