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