/* * Copyright (C) 2012 Google Inc. * Licensed to The Android Open Source Project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mail.preferences; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import android.app.backup.BackupManager; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import com.android.mail.MailIntentService; import com.android.mail.utils.LogTag; import com.android.mail.utils.LogUtils; import java.util.List; import java.util.Map; import java.util.Set; /** * A high-level API to store and retrieve preferences, that can be versioned in a similar manner as * SQLite databases. You must not use the preference key * {@value VersionedPrefs#PREFS_VERSION_NUMBER} */ public abstract class VersionedPrefs { private final Context mContext; private final String mSharedPreferencesName; private final SharedPreferences mSharedPreferences; private final Editor mEditor; /** The key for the version number of the {@link SharedPreferences} file. */ private static final String PREFS_VERSION_NUMBER = "prefs-version-number"; /** * The current version number for {@link SharedPreferences}. This is a constant for all * applications based on UnifiedEmail. */ protected static final int CURRENT_VERSION_NUMBER = 4; protected static final String LOG_TAG = LogTag.getLogTag(); /** * @param sharedPrefsName The name of the {@link SharedPreferences} file to use */ protected VersionedPrefs(final Context context, final String sharedPrefsName) { mContext = context.getApplicationContext(); mSharedPreferencesName = sharedPrefsName; mSharedPreferences = context.getSharedPreferences(sharedPrefsName, Context.MODE_PRIVATE); mEditor = mSharedPreferences.edit(); final int oldVersion = getCurrentVersion(); performUpgrade(oldVersion, CURRENT_VERSION_NUMBER); setCurrentVersion(CURRENT_VERSION_NUMBER); if (!hasMigrationCompleted()) { final BasePreferenceMigrator preferenceMigrator = PreferenceMigratorHolder.createPreferenceMigrator(); final boolean migrationComplete; if (preferenceMigrator != null) { migrationComplete = preferenceMigrator .performMigration(context, oldVersion, CURRENT_VERSION_NUMBER); } else { LogUtils.w(LogUtils.TAG, "No preference migrator found, not migrating preferences"); migrationComplete = false; } if (migrationComplete) { setMigrationComplete(); } } } protected Context getContext() { return mContext; } public String getSharedPreferencesName() { return mSharedPreferencesName; } protected SharedPreferences getSharedPreferences() { return mSharedPreferences; } protected Editor getEditor() { return mEditor; } public void registerOnSharedPreferenceChangeListener( SharedPreferences.OnSharedPreferenceChangeListener listener) { mSharedPreferences.registerOnSharedPreferenceChangeListener(listener); } public void unregisterOnSharedPreferenceChangeListener( SharedPreferences.OnSharedPreferenceChangeListener listener) { mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener); } /** * Returns the current version of the {@link SharedPreferences} file. */ private int getCurrentVersion() { return mSharedPreferences.getInt(PREFS_VERSION_NUMBER, 0); } private void setCurrentVersion(final int versionNumber) { getEditor().putInt(PREFS_VERSION_NUMBER, versionNumber); /* * If the only preference we have is the version number, we do not want to commit it. * Instead, we will wait for some other preference to be written. This prevents us from * creating a file with only the version number. */ if (shouldBackUp()) { getEditor().apply(); } } protected boolean hasMigrationCompleted() { return MailPrefs.get(mContext).hasMigrationCompleted(); } protected void setMigrationComplete() { MailPrefs.get(mContext).setMigrationComplete(); } /** * Commits all pending changes to the preferences. */ public void commit() { getEditor().commit(); } /** * Upgrades the {@link SharedPreferences} file. * * @param oldVersion The current version * @param newVersion The new version */ protected abstract void performUpgrade(int oldVersion, int newVersion); @VisibleForTesting public void clearAllPreferences() { getEditor().clear().commit(); } protected abstract boolean canBackup(String key); /** * Gets the value to backup for a given key-value pair. By default, returns the passed in value. * * @param key The key to backup * @param value The locally stored value for the given key * @return The value to backup */ protected Object getBackupValue(final String key, final Object value) { return value; } /** * Gets the value to restore for a given key-value pair. By default, returns the passed in * value. * * @param key The key to restore * @param value The backed up value for the given key * @return The value to restore */ protected Object getRestoreValue(final String key, final Object value) { return value; } /** * Return a list of shared preferences that should be backed up. */ public List getBackupPreferences() { final List backupPreferences = Lists.newArrayList(); final SharedPreferences sharedPreferences = getSharedPreferences(); final Map preferences = sharedPreferences.getAll(); for (final Map.Entry entry : preferences.entrySet()) { final String key = entry.getKey(); if (!canBackup(key)) { continue; } final Object value = entry.getValue(); final Object backupValue = getBackupValue(key, value); if (backupValue != null) { backupPreferences.add(new SimpleBackupSharedPreference(key, backupValue)); } } return backupPreferences; } /** * Restores preferences from a backup. */ public void restorePreferences(final List preferences) { for (final BackupSharedPreference preference : preferences) { final String key = preference.getKey(); final Object value = preference.getValue(); if (!canBackup(key) || value == null) { continue; } final Object restoreValue = getRestoreValue(key, value); if (restoreValue instanceof Boolean) { getEditor().putBoolean(key, (Boolean) restoreValue); LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference); } else if (restoreValue instanceof Float) { getEditor().putFloat(key, (Float) restoreValue); LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference); } else if (restoreValue instanceof Integer) { getEditor().putInt(key, (Integer) restoreValue); LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference); } else if (restoreValue instanceof Long) { getEditor().putLong(key, (Long) restoreValue); LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference); } else if (restoreValue instanceof String) { getEditor().putString(key, (String) restoreValue); LogUtils.v(LOG_TAG, "MailPrefs Restore: %s", preference); } else if (restoreValue instanceof Set) { getEditor().putStringSet(key, (Set) restoreValue); } else { LogUtils.e(LOG_TAG, "Unknown MailPrefs preference data type: %s", value.getClass()); } } getEditor().apply(); } /** *

* Checks if any of the preferences eligible for backup have been modified from their default * values, and therefore should be backed up. *

* * @return true if anything has been modified, false otherwise */ public boolean shouldBackUp() { final Map allPrefs = getSharedPreferences().getAll(); for (final String key : allPrefs.keySet()) { if (canBackup(key)) { return true; } } return false; } /** * Notifies {@link BackupManager} that we have new data to back up. */ protected void notifyBackupPreferenceChanged() { MailIntentService.broadcastBackupDataChanged(getContext()); } }