Home | History | Annotate | Download | only in preferences
      1 /*
      2  * Copyright (C) 2012 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mail.preferences;
     19 
     20 import com.google.common.annotations.VisibleForTesting;
     21 import com.google.common.collect.Lists;
     22 
     23 import android.app.backup.BackupManager;
     24 import android.content.Context;
     25 import android.content.SharedPreferences;
     26 import android.content.SharedPreferences.Editor;
     27 
     28 import com.android.mail.MailIntentService;
     29 import com.android.mail.utils.LogTag;
     30 import com.android.mail.utils.LogUtils;
     31 
     32 import java.util.List;
     33 import java.util.Map;
     34 import java.util.Set;
     35 
     36 /**
     37  * A high-level API to store and retrieve preferences, that can be versioned in a similar manner as
     38  * SQLite databases. You must not use the preference key
     39  * {@value VersionedPrefs#PREFS_VERSION_NUMBER}
     40  */
     41 public abstract class VersionedPrefs {
     42     private final Context mContext;
     43     private final String mSharedPreferencesName;
     44     private final SharedPreferences mSharedPreferences;
     45     private final Editor mEditor;
     46 
     47     /** The key for the version number of the {@link SharedPreferences} file. */
     48     private static final String PREFS_VERSION_NUMBER = "prefs-version-number";
     49 
     50     /**
     51      * The current version number for {@link SharedPreferences}. This is a constant for all
     52      * applications based on UnifiedEmail.
     53      */
     54     protected static final int CURRENT_VERSION_NUMBER = 3;
     55 
     56     protected static final String LOG_TAG = LogTag.getLogTag();
     57 
     58     /**
     59      * @param sharedPrefsName The name of the {@link SharedPreferences} file to use
     60      */
     61     protected VersionedPrefs(final Context context, final String sharedPrefsName) {
     62         mContext = context.getApplicationContext();
     63         mSharedPreferencesName = sharedPrefsName;
     64         mSharedPreferences = context.getSharedPreferences(sharedPrefsName, Context.MODE_PRIVATE);
     65         mEditor = mSharedPreferences.edit();
     66 
     67         final int oldVersion = getCurrentVersion();
     68 
     69         performUpgrade(oldVersion, CURRENT_VERSION_NUMBER);
     70         setCurrentVersion(CURRENT_VERSION_NUMBER);
     71 
     72         if (!hasMigrationCompleted()) {
     73             final boolean migrationComplete = PreferenceMigratorHolder.createPreferenceMigrator()
     74                     .performMigration(context, oldVersion, CURRENT_VERSION_NUMBER);
     75 
     76             if (migrationComplete) {
     77                 setMigrationComplete();
     78             }
     79         }
     80     }
     81 
     82     protected Context getContext() {
     83         return mContext;
     84     }
     85 
     86     public String getSharedPreferencesName() {
     87         return mSharedPreferencesName;
     88     }
     89 
     90     protected SharedPreferences getSharedPreferences() {
     91         return mSharedPreferences;
     92     }
     93 
     94     protected Editor getEditor() {
     95         return mEditor;
     96     }
     97 
     98     /**
     99      * Returns the current version of the {@link SharedPreferences} file.
    100      */
    101     private int getCurrentVersion() {
    102         return mSharedPreferences.getInt(PREFS_VERSION_NUMBER, 0);
    103     }
    104 
    105     private void setCurrentVersion(final int versionNumber) {
    106         getEditor().putInt(PREFS_VERSION_NUMBER, versionNumber);
    107 
    108         /*
    109          * If the only preference we have is the version number, we do not want to commit it.
    110          * Instead, we will wait for some other preference to be written. This prevents us from
    111          * creating a file with only the version number.
    112          */
    113         if (shouldBackUp()) {
    114             getEditor().apply();
    115         }
    116     }
    117 
    118     protected boolean hasMigrationCompleted() {
    119         return MailPrefs.get(mContext).hasMigrationCompleted();
    120     }
    121 
    122     protected void setMigrationComplete() {
    123         MailPrefs.get(mContext).setMigrationComplete();
    124     }
    125 
    126     /**
    127      * Commits all pending changes to the preferences.
    128      */
    129     public void commit() {
    130         getEditor().commit();
    131     }
    132 
    133     /**
    134      * Upgrades the {@link SharedPreferences} file.
    135      *
    136      * @param oldVersion The current version
    137      * @param newVersion The new version
    138      */
    139     protected abstract void performUpgrade(int oldVersion, int newVersion);
    140 
    141     @VisibleForTesting
    142     public void clearAllPreferences() {
    143         getEditor().clear().commit();
    144     }
    145 
    146     protected abstract boolean canBackup(String key);
    147 
    148     /**
    149      * Gets the value to backup for a given key-value pair. By default, returns the passed in value.
    150      *
    151      * @param key The key to backup
    152      * @param value The locally stored value for the given key
    153      * @return The value to backup
    154      */
    155     protected Object getBackupValue(final String key, final Object value) {
    156         return value;
    157     }
    158 
    159     /**
    160      * Gets the value to restore for a given key-value pair. By default, returns the passed in
    161      * value.
    162      *
    163      * @param key The key to restore
    164      * @param value The backed up value for the given key
    165      * @return The value to restore
    166      */
    167     protected Object getRestoreValue(final String key, final Object value) {
    168         return value;
    169     }
    170 
    171     /**
    172      * Return a list of shared preferences that should be backed up.
    173      */
    174     public List<BackupSharedPreference> getBackupPreferences() {
    175         final List<BackupSharedPreference> backupPreferences = Lists.newArrayList();
    176         final SharedPreferences sharedPreferences = getSharedPreferences();
    177         final Map<String, ?> preferences = sharedPreferences.getAll();
    178 
    179         for (final Map.Entry<String, ?> entry : preferences.entrySet()) {
    180             final String key = entry.getKey();
    181 
    182             if (!canBackup(key)) {
    183                 continue;
    184             }
    185 
    186             final Object value = entry.getValue();
    187             final Object backupValue = getBackupValue(key, value);
    188 
    189             if (backupValue != null) {
    190                 backupPreferences.add(new SimpleBackupSharedPreference(key, backupValue));
    191             }
    192         }
    193 
    194         return backupPreferences;
    195     }
    196 
    197     /**
    198      * Restores preferences from a backup.
    199      */
    200     public void restorePreferences(final List<BackupSharedPreference> preferences) {
    201         for (final BackupSharedPreference preference : preferences) {
    202             final String key = preference.getKey();
    203             final Object value = preference.getValue();
    204 
    205             if (!canBackup(key) || value == null) {
    206                 continue;
    207             }
    208 
    209             final Object restoreValue = getRestoreValue(key, value);
    210 
    211             if (restoreValue instanceof Boolean) {
    212                 getEditor().putBoolean(key, (Boolean) restoreValue);
    213                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    214             } else if (restoreValue instanceof Float) {
    215                 getEditor().putFloat(key, (Float) restoreValue);
    216                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    217             } else if (restoreValue instanceof Integer) {
    218                 getEditor().putInt(key, (Integer) restoreValue);
    219                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    220             } else if (restoreValue instanceof Long) {
    221                 getEditor().putLong(key, (Long) restoreValue);
    222                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    223             } else if (restoreValue instanceof String) {
    224                 getEditor().putString(key, (String) restoreValue);
    225                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    226             } else if (restoreValue instanceof Set) {
    227                 getEditor().putStringSet(key, (Set<String>) restoreValue);
    228             } else {
    229                 LogUtils.e(LOG_TAG, "Unknown MailPrefs preference data type: %s", value.getClass());
    230             }
    231         }
    232 
    233         getEditor().apply();
    234     }
    235 
    236     /**
    237      * <p>
    238      * Checks if any of the preferences eligible for backup have been modified from their default
    239      * values, and therefore should be backed up.
    240      * </p>
    241      *
    242      * @return <code>true</code> if anything has been modified, <code>false</code> otherwise
    243      */
    244     public boolean shouldBackUp() {
    245         final Map<String, ?> allPrefs = getSharedPreferences().getAll();
    246 
    247         for (final String key : allPrefs.keySet()) {
    248             if (canBackup(key)) {
    249                 return true;
    250             }
    251         }
    252 
    253         return false;
    254     }
    255 
    256     /**
    257      * Notifies {@link BackupManager} that we have new data to back up.
    258      */
    259     protected void notifyBackupPreferenceChanged() {
    260         MailIntentService.broadcastBackupDataChanged(getContext());
    261     }
    262 }
    263