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 17 package com.android.email.activity.setup; 18 19 import android.app.ActionBar; 20 import android.app.Activity; 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.res.Resources; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.preference.CheckBoxPreference; 29 import android.preference.ListPreference; 30 import android.preference.Preference; 31 import android.preference.Preference.OnPreferenceChangeListener; 32 import android.preference.PreferenceActivity; 33 import android.view.MenuItem; 34 35 import com.android.email.R; 36 import com.android.emailcommon.Logging; 37 import com.android.emailcommon.provider.Account; 38 import com.android.emailcommon.provider.EmailContent.MailboxColumns; 39 import com.android.emailcommon.provider.Mailbox; 40 import com.android.emailcommon.provider.Policy; 41 import com.android.emailcommon.utility.EmailAsyncTask; 42 import com.android.emailcommon.utility.Utility; 43 import com.android.mail.utils.LogUtils; 44 import com.google.common.base.Preconditions; 45 46 import java.util.Arrays; 47 48 /** 49 * "Mailbox settings" activity. 50 * 51 * It's used to update per-mailbox sync settings. It normally updates Mailbox settings, unless 52 * the target mailbox is Inbox, in which case it updates Account settings instead. 53 * 54 * All changes made by the user will not be immediately saved to the database, as changing the 55 * sync window may result in removal of messages. Instead, we only save to the database in {@link 56 * #onDestroy()}, unless it's called for configuration changes. 57 */ 58 public class MailboxSettings extends PreferenceActivity { 59 private static final String EXTRA_MAILBOX_ID = "MAILBOX_ID"; 60 private static final String BUNDLE_MAILBOX = "MailboxSettings.mailbox"; 61 private static final String BUNDLE_MAX_LOOKBACK = "MailboxSettings.maxLookback"; 62 private static final String BUNDLE_SYNC_ENABLED_VALUE = "MailboxSettings.syncEnabled"; 63 private static final String BUNDLE_SYNC_WINDOW_VALUE = "MailboxSettings.syncWindow"; 64 65 private static final String PREF_SYNC_ENABLED_KEY = "sync_enabled"; 66 private static final String PREF_SYNC_WINDOW_KEY = "sync_window"; 67 68 /** Projection for loading an account's policy key. */ 69 private static final String[] POLICY_KEY_PROJECTION = { Account.POLICY_KEY }; 70 private static final int POLICY_KEY_COLUMN = 0; 71 72 /** Projection for loading the max email lookback. */ 73 private static final String[] MAX_EMAIL_LOOKBACK_PROJECTION = { Policy.MAX_EMAIL_LOOKBACK }; 74 private static final int MAX_EMAIL_LOOKBACK_COLUMN = 0; 75 76 private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); 77 78 private Mailbox mMailbox; 79 /** The maximum lookback allowed for this mailbox, or 0 if no max. */ 80 private int mMaxLookback; 81 82 private CheckBoxPreference mSyncEnabledPref; 83 private ListPreference mSyncLookbackPref; 84 85 /** 86 * Starts the activity for a mailbox. 87 */ 88 public static final void start(Activity parent, long mailboxId) { 89 Intent i = new Intent(parent, MailboxSettings.class); 90 i.putExtra(EXTRA_MAILBOX_ID, mailboxId); 91 parent.startActivity(i); 92 } 93 94 @Override 95 protected void onCreate(Bundle savedInstanceState) { 96 super.onCreate(savedInstanceState); 97 98 final long mailboxId = getIntent().getLongExtra(EXTRA_MAILBOX_ID, Mailbox.NO_MAILBOX); 99 if (mailboxId == Mailbox.NO_MAILBOX) { 100 finish(); 101 return; 102 } 103 104 addPreferencesFromResource(R.xml.mailbox_preferences); 105 106 mSyncEnabledPref = (CheckBoxPreference) findPreference(PREF_SYNC_ENABLED_KEY); 107 mSyncLookbackPref = (ListPreference) findPreference(PREF_SYNC_WINDOW_KEY); 108 109 mSyncLookbackPref.setOnPreferenceChangeListener(mPreferenceChanged); 110 111 if (savedInstanceState != null) { 112 mMailbox = savedInstanceState.getParcelable(BUNDLE_MAILBOX); 113 mMaxLookback = savedInstanceState.getInt(BUNDLE_MAX_LOOKBACK); 114 mSyncEnabledPref.setChecked(savedInstanceState.getBoolean(BUNDLE_SYNC_ENABLED_VALUE)); 115 mSyncLookbackPref.setValue(savedInstanceState.getString(BUNDLE_SYNC_WINDOW_VALUE)); 116 onDataLoaded(); 117 } else { 118 // Make them disabled until we load data 119 enablePreferences(false); 120 new LoadMailboxTask(mailboxId).executeParallel((Void[]) null); 121 } 122 123 // Always show "app up" as we expect our parent to be an Email activity. 124 ActionBar actionBar = getActionBar(); 125 if (actionBar != null) { 126 actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP); 127 } 128 } 129 130 private void enablePreferences(boolean enabled) { 131 mSyncEnabledPref.setEnabled(enabled); 132 mSyncLookbackPref.setEnabled(enabled); 133 } 134 135 @Override 136 protected void onSaveInstanceState(Bundle outState) { 137 super.onSaveInstanceState(outState); 138 outState.putParcelable(BUNDLE_MAILBOX, mMailbox); 139 outState.putInt(BUNDLE_MAX_LOOKBACK, mMaxLookback); 140 outState.putBoolean(BUNDLE_SYNC_ENABLED_VALUE, mSyncEnabledPref.isChecked()); 141 outState.putString(BUNDLE_SYNC_WINDOW_VALUE, mSyncLookbackPref.getValue()); 142 } 143 144 /** 145 * We save all the settings in onDestroy, *unless it's for configuration changes*. 146 */ 147 @Override 148 protected void onDestroy() { 149 mTaskTracker.cancellAllInterrupt(); 150 if (!isChangingConfigurations()) { 151 saveToDatabase(); 152 } 153 super.onDestroy(); 154 } 155 156 /** 157 * Loads {@link #mMailbox} and {@link #mMaxLookback} from DB. 158 */ 159 private class LoadMailboxTask extends EmailAsyncTask<Void, Void, Void> { 160 private final long mMailboxId; 161 162 public LoadMailboxTask(long mailboxId) { 163 super(mTaskTracker); 164 mMailboxId = mailboxId; 165 } 166 167 @Override 168 protected Void doInBackground(Void... params) { 169 final Context c = MailboxSettings.this; 170 mMailbox = Mailbox.restoreMailboxWithId(c, mMailboxId); 171 mMaxLookback = 0; 172 if (mMailbox != null) { 173 // Get the max lookback from our policy, if we have one. 174 final Long policyKey = Utility.getFirstRowLong(c, ContentUris.withAppendedId( 175 Account.CONTENT_URI, mMailbox.mAccountKey), POLICY_KEY_PROJECTION, 176 null, null, null, POLICY_KEY_COLUMN); 177 if (policyKey != null) { 178 mMaxLookback = Utility.getFirstRowInt(c, ContentUris.withAppendedId( 179 Policy.CONTENT_URI, policyKey), MAX_EMAIL_LOOKBACK_PROJECTION, 180 null, null, null, MAX_EMAIL_LOOKBACK_COLUMN, 0); 181 } 182 } 183 return null; 184 } 185 186 @Override 187 protected void onSuccess(Void result) { 188 if (mMailbox == null) { 189 finish(); // Account or mailbox removed. 190 return; 191 } 192 mSyncEnabledPref.setChecked(mMailbox.mSyncInterval != 0); 193 mSyncLookbackPref.setValue(String.valueOf(mMailbox.mSyncLookback)); 194 onDataLoaded(); 195 if (mMailbox.mType != Mailbox.TYPE_DRAFTS) { 196 enablePreferences(true); 197 } 198 } 199 } 200 201 /** 202 * Setup the entries and entry values for the sync lookback preference 203 * @param context the caller's context 204 * @param pref a ListPreference to be set up 205 * @param maxLookback The maximum lookback allowed, or 0 if no max. 206 * @param showWithDefault Whether to show the version with default, or without. 207 */ 208 public static void setupLookbackPreferenceOptions(final Context context, 209 final ListPreference pref, final int maxLookback, final boolean showWithDefault) { 210 final Resources resources = context.getResources(); 211 // Load the complete list of entries/values 212 CharSequence[] entries; 213 CharSequence[] values; 214 final int offset; 215 if (showWithDefault) { 216 entries = resources.getTextArray( 217 R.array.account_settings_mail_window_entries_with_default); 218 values = resources.getTextArray( 219 R.array.account_settings_mail_window_values_with_default); 220 offset = 1; 221 } else { 222 entries = resources.getTextArray(R.array.account_settings_mail_window_entries); 223 values = resources.getTextArray(R.array.account_settings_mail_window_values); 224 offset = 0; 225 } 226 // If we have a maximum lookback policy, enforce it 227 if (maxLookback > 0) { 228 final int size = maxLookback + offset; 229 entries = Arrays.copyOf(entries, size); 230 values = Arrays.copyOf(values, size); 231 } 232 // Set up the preference 233 pref.setEntries(entries); 234 pref.setEntryValues(values); 235 pref.setSummary(pref.getEntry()); 236 } 237 238 /** 239 * Called when {@link #mMailbox} is loaded (either by the async task or from the saved state). 240 */ 241 private void onDataLoaded() { 242 Preconditions.checkNotNull(mMailbox); 243 244 // Update the title with the mailbox name. 245 final ActionBar actionBar = getActionBar(); 246 final String mailboxName = mMailbox.mDisplayName; 247 if (actionBar != null) { 248 actionBar.setTitle(mailboxName); 249 actionBar.setSubtitle(getString(R.string.mailbox_settings_activity_title)); 250 } else { 251 setTitle(getString(R.string.mailbox_settings_activity_title_with_mailbox, mailboxName)); 252 } 253 254 setupLookbackPreferenceOptions(this, mSyncLookbackPref, mMaxLookback, true); 255 } 256 257 258 private final OnPreferenceChangeListener mPreferenceChanged = new OnPreferenceChangeListener() { 259 @Override 260 public boolean onPreferenceChange(Preference preference, Object newValue) { 261 mSyncLookbackPref.setValue((String) newValue); 262 mSyncLookbackPref.setSummary(mSyncLookbackPref.getEntry()); 263 return false; 264 } 265 }; 266 267 /** 268 * Save changes to the database. 269 * 270 * Note it's called from {@link #onDestroy()}, which is called on the UI thread where we're not 271 * allowed to touch the database, so it uses {@link EmailAsyncTask} to do the save on a bg 272 * thread. This unfortunately means there's a chance that the app gets killed before the save is 273 * finished. 274 */ 275 private void saveToDatabase() { 276 final int syncInterval = mSyncEnabledPref.isChecked() ? 1 : 0; 277 final int syncLookback = Integer.valueOf(mSyncLookbackPref.getValue()); 278 279 final boolean syncIntervalChanged = syncInterval != mMailbox.mSyncInterval; 280 final boolean syncLookbackChanged = syncLookback != mMailbox.mSyncLookback; 281 282 // Only save if a preference has changed value. 283 if (syncIntervalChanged || syncLookbackChanged) { 284 LogUtils.i(Logging.LOG_TAG, "Saving mailbox settings..."); 285 enablePreferences(false); 286 287 final long id = mMailbox.mId; 288 final Context context = getApplicationContext(); 289 290 new EmailAsyncTask<Void, Void, Void> (null /* no cancel */) { 291 @Override 292 protected Void doInBackground(Void... params) { 293 final ContentValues cv = new ContentValues(2); 294 final Uri uri; 295 if (syncIntervalChanged) { 296 cv.put(MailboxColumns.SYNC_INTERVAL, syncInterval); 297 } 298 if (syncLookbackChanged) { 299 cv.put(MailboxColumns.SYNC_LOOKBACK, syncLookback); 300 } 301 uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, id); 302 context.getContentResolver().update(uri, cv, null, null); 303 304 LogUtils.i(Logging.LOG_TAG, "Saved: " + uri); 305 return null; 306 } 307 308 @Override 309 protected void onSuccess(Void result) { 310 // must be called on the ui thread 311 //*** 312 //RefreshManager.getInstance(context).refreshMessageList(account.mId, 313 // mailbox.mId, true); 314 } 315 }.executeSerial((Void [])null); 316 } 317 } 318 319 @Override 320 public boolean onOptionsItemSelected(MenuItem item) { 321 if (item.getItemId() == android.R.id.home) { 322 onBackPressed(); 323 return true; 324 } 325 return super.onOptionsItemSelected(item); 326 } 327 } 328