MailboxSettings.java revision 919e59044b5c6a3d3947af05ddf241f51f65def4
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.content.res.Resources;
26import android.net.Uri;
27import android.os.Bundle;
28import android.preference.ListPreference;
29import android.preference.Preference;
30import android.preference.Preference.OnPreferenceChangeListener;
31import android.preference.PreferenceActivity;
32import android.util.Log;
33import android.view.MenuItem;
34
35import com.android.email.Email;
36import com.android.email.FolderProperties;
37import com.android.email.R;
38import com.android.email.RefreshManager;
39import com.android.emailcommon.Logging;
40import com.android.emailcommon.provider.Account;
41import com.android.emailcommon.provider.Policy;
42import com.android.emailcommon.provider.EmailContent.AccountColumns;
43import com.android.emailcommon.provider.EmailContent.MailboxColumns;
44import com.android.emailcommon.provider.Mailbox;
45import com.android.emailcommon.utility.EmailAsyncTask;
46import com.google.common.base.Objects;
47import 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 */
59public 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     */
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
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
126    private void enablePreferences(boolean enabled) {
127        mSyncIntervalPref.setEnabled(enabled);
128        mSyncLookbackPref.setEnabled(enabled);
129    }
130
131    @Override
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
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
157        public LoadMailboxTask(long mailboxId) {
158            super(mTaskTracker);
159            mMailboxId = mailboxId;
160        }
161
162        @Override
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
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     */
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     */
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
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     */
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     */
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     */
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     */
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
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