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 = 4;
     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 BasePreferenceMigrator preferenceMigrator =
     74                     PreferenceMigratorHolder.createPreferenceMigrator();
     75             final boolean migrationComplete;
     76             if (preferenceMigrator != null) {
     77                 migrationComplete = preferenceMigrator
     78                         .performMigration(context, oldVersion, CURRENT_VERSION_NUMBER);
     79             } else {
     80                 LogUtils.w(LogUtils.TAG, "No preference migrator found, not migrating preferences");
     81                 migrationComplete = false;
     82             }
     83 
     84             if (migrationComplete) {
     85                 setMigrationComplete();
     86             }
     87         }
     88     }
     89 
     90     protected Context getContext() {
     91         return mContext;
     92     }
     93 
     94     public String getSharedPreferencesName() {
     95         return mSharedPreferencesName;
     96     }
     97 
     98     protected SharedPreferences getSharedPreferences() {
     99         return mSharedPreferences;
    100     }
    101 
    102     protected Editor getEditor() {
    103         return mEditor;
    104     }
    105 
    106     public void registerOnSharedPreferenceChangeListener(
    107             SharedPreferences.OnSharedPreferenceChangeListener listener) {
    108         mSharedPreferences.registerOnSharedPreferenceChangeListener(listener);
    109     }
    110 
    111     public void unregisterOnSharedPreferenceChangeListener(
    112             SharedPreferences.OnSharedPreferenceChangeListener listener) {
    113         mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
    114     }
    115 
    116     /**
    117      * Returns the current version of the {@link SharedPreferences} file.
    118      */
    119     private int getCurrentVersion() {
    120         return mSharedPreferences.getInt(PREFS_VERSION_NUMBER, 0);
    121     }
    122 
    123     private void setCurrentVersion(final int versionNumber) {
    124         getEditor().putInt(PREFS_VERSION_NUMBER, versionNumber);
    125 
    126         /*
    127          * If the only preference we have is the version number, we do not want to commit it.
    128          * Instead, we will wait for some other preference to be written. This prevents us from
    129          * creating a file with only the version number.
    130          */
    131         if (shouldBackUp()) {
    132             getEditor().apply();
    133         }
    134     }
    135 
    136     protected boolean hasMigrationCompleted() {
    137         return MailPrefs.get(mContext).hasMigrationCompleted();
    138     }
    139 
    140     protected void setMigrationComplete() {
    141         MailPrefs.get(mContext).setMigrationComplete();
    142     }
    143 
    144     /**
    145      * Commits all pending changes to the preferences.
    146      */
    147     public void commit() {
    148         getEditor().commit();
    149     }
    150 
    151     /**
    152      * Upgrades the {@link SharedPreferences} file.
    153      *
    154      * @param oldVersion The current version
    155      * @param newVersion The new version
    156      */
    157     protected abstract void performUpgrade(int oldVersion, int newVersion);
    158 
    159     @VisibleForTesting
    160     public void clearAllPreferences() {
    161         getEditor().clear().commit();
    162     }
    163 
    164     protected abstract boolean canBackup(String key);
    165 
    166     /**
    167      * Gets the value to backup for a given key-value pair. By default, returns the passed in value.
    168      *
    169      * @param key The key to backup
    170      * @param value The locally stored value for the given key
    171      * @return The value to backup
    172      */
    173     protected Object getBackupValue(final String key, final Object value) {
    174         return value;
    175     }
    176 
    177     /**
    178      * Gets the value to restore for a given key-value pair. By default, returns the passed in
    179      * value.
    180      *
    181      * @param key The key to restore
    182      * @param value The backed up value for the given key
    183      * @return The value to restore
    184      */
    185     protected Object getRestoreValue(final String key, final Object value) {
    186         return value;
    187     }
    188 
    189     /**
    190      * Return a list of shared preferences that should be backed up.
    191      */
    192     public List<BackupSharedPreference> getBackupPreferences() {
    193         final List<BackupSharedPreference> backupPreferences = Lists.newArrayList();
    194         final SharedPreferences sharedPreferences = getSharedPreferences();
    195         final Map<String, ?> preferences = sharedPreferences.getAll();
    196 
    197         for (final Map.Entry<String, ?> entry : preferences.entrySet()) {
    198             final String key = entry.getKey();
    199 
    200             if (!canBackup(key)) {
    201                 continue;
    202             }
    203 
    204             final Object value = entry.getValue();
    205             final Object backupValue = getBackupValue(key, value);
    206 
    207             if (backupValue != null) {
    208                 backupPreferences.add(new SimpleBackupSharedPreference(key, backupValue));
    209             }
    210         }
    211 
    212         return backupPreferences;
    213     }
    214 
    215     /**
    216      * Restores preferences from a backup.
    217      */
    218     public void restorePreferences(final List<BackupSharedPreference> preferences) {
    219         for (final BackupSharedPreference preference : preferences) {
    220             final String key = preference.getKey();
    221             final Object value = preference.getValue();
    222 
    223             if (!canBackup(key) || value == null) {
    224                 continue;
    225             }
    226 
    227             final Object restoreValue = getRestoreValue(key, value);
    228 
    229             if (restoreValue instanceof Boolean) {
    230                 getEditor().putBoolean(key, (Boolean) restoreValue);
    231                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    232             } else if (restoreValue instanceof Float) {
    233                 getEditor().putFloat(key, (Float) restoreValue);
    234                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    235             } else if (restoreValue instanceof Integer) {
    236                 getEditor().putInt(key, (Integer) restoreValue);
    237                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    238             } else if (restoreValue instanceof Long) {
    239                 getEditor().putLong(key, (Long) restoreValue);
    240                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    241             } else if (restoreValue instanceof String) {
    242                 getEditor().putString(key, (String) restoreValue);
    243                 LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference);
    244             } else if (restoreValue instanceof Set) {
    245                 getEditor().putStringSet(key, (Set<String>) restoreValue);
    246             } else {
    247                 LogUtils.e(LOG_TAG, "Unknown MailPrefs preference data type: %s", value.getClass());
    248             }
    249         }
    250 
    251         getEditor().apply();
    252     }
    253 
    254     /**
    255      * <p>
    256      * Checks if any of the preferences eligible for backup have been modified from their default
    257      * values, and therefore should be backed up.
    258      * </p>
    259      *
    260      * @return <code>true</code> if anything has been modified, <code>false</code> otherwise
    261      */
    262     public boolean shouldBackUp() {
    263         final Map<String, ?> allPrefs = getSharedPreferences().getAll();
    264 
    265         for (final String key : allPrefs.keySet()) {
    266             if (canBackup(key)) {
    267                 return true;
    268             }
    269         }
    270 
    271         return false;
    272     }
    273 
    274     /**
    275      * Notifies {@link BackupManager} that we have new data to back up.
    276      */
    277     protected void notifyBackupPreferenceChanged() {
    278         MailIntentService.broadcastBackupDataChanged(getContext());
    279     }
    280 }
    281