• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
VersionedPrefs(final Context context, final String sharedPrefsName)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 
getContext()82     protected Context getContext() {
83         return mContext;
84     }
85 
getSharedPreferencesName()86     public String getSharedPreferencesName() {
87         return mSharedPreferencesName;
88     }
89 
getSharedPreferences()90     protected SharedPreferences getSharedPreferences() {
91         return mSharedPreferences;
92     }
93 
getEditor()94     protected Editor getEditor() {
95         return mEditor;
96     }
97 
98     /**
99      * Returns the current version of the {@link SharedPreferences} file.
100      */
getCurrentVersion()101     private int getCurrentVersion() {
102         return mSharedPreferences.getInt(PREFS_VERSION_NUMBER, 0);
103     }
104 
setCurrentVersion(final int versionNumber)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 
hasMigrationCompleted()118     protected boolean hasMigrationCompleted() {
119         return MailPrefs.get(mContext).hasMigrationCompleted();
120     }
121 
setMigrationComplete()122     protected void setMigrationComplete() {
123         MailPrefs.get(mContext).setMigrationComplete();
124     }
125 
126     /**
127      * Commits all pending changes to the preferences.
128      */
commit()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      */
performUpgrade(int oldVersion, int newVersion)139     protected abstract void performUpgrade(int oldVersion, int newVersion);
140 
141     @VisibleForTesting
clearAllPreferences()142     public void clearAllPreferences() {
143         getEditor().clear().commit();
144     }
145 
canBackup(String key)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      */
getBackupValue(final String key, final Object value)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      */
getRestoreValue(final String key, final Object value)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      */
getBackupPreferences()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      */
restorePreferences(final List<BackupSharedPreference> preferences)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      */
shouldBackUp()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      */
notifyBackupPreferenceChanged()259     protected void notifyBackupPreferenceChanged() {
260         MailIntentService.broadcastBackupDataChanged(getContext());
261     }
262 }
263