Home | History | Annotate | Download | only in setup
      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      */
     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