Bug 940575 - Implement scopes for SharedPreferences (r=rnewman)

This commit is contained in:
Lucas Rocha 2014-04-01 15:02:14 +01:00
Родитель dc23abbc03
Коммит f1fc78b9e8
14 изменённых файлов: 495 добавлений и 35 удалений

Просмотреть файл

@ -133,8 +133,7 @@ public class CrashReporter extends Activity
// Set the flag that indicates we were stopped as expected, as
// we will send a crash report, so it is not a silent OOM crash.
SharedPreferences prefs =
getSharedPreferences(GeckoApp.PREFS_NAME, 0);
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
editor.commit();
@ -231,8 +230,7 @@ public class CrashReporter extends Activity
}
private void savePrefs() {
SharedPreferences prefs = getSharedPreferences(GeckoApp.PREFS_NAME, 0);
SharedPreferences.Editor editor = prefs.edit();
SharedPreferences.Editor editor = GeckoSharedPrefs.forApp(this).edit();
final boolean allowContact = ((CheckBox) findViewById(R.id.allow_contact)).isChecked();
final boolean includeUrl = ((CheckBox) findViewById(R.id.include_url)).isChecked();

Просмотреть файл

@ -26,13 +26,12 @@ public class DataReportingNotification {
public static final String ALERT_NAME_DATAREPORTING_NOTIFICATION = "datareporting-notification";
private static final String DEFAULT_PREFS_BRANCH = AppConstants.ANDROID_PACKAGE_NAME + "_preferences";
private static final String PREFS_POLICY_NOTIFIED_TIME = "datareporting.policy.dataSubmissionPolicyNotifiedTime";
private static final String PREFS_POLICY_VERSION = "datareporting.policy.dataSubmissionPolicyVersion";
private static final int DATA_REPORTING_VERSION = 1;
public static void checkAndNotifyPolicy(Context context) {
SharedPreferences dataPrefs = context.getSharedPreferences(DEFAULT_PREFS_BRANCH, 0);
SharedPreferences dataPrefs = GeckoSharedPrefs.forApp(context);
// Notify if user has not been notified or if policy version has changed.
if ((!dataPrefs.contains(PREFS_POLICY_NOTIFIED_TIME)) ||

Просмотреть файл

@ -34,8 +34,6 @@ import java.util.zip.ZipFile;
public final class Distribution {
private static final String LOGTAG = "GeckoDistribution";
private static final String DEFAULT_PREFS = GeckoApp.PREFS_NAME;
private static final int STATE_UNKNOWN = 0;
private static final int STATE_NONE = 1;
private static final int STATE_SET = 2;
@ -108,7 +106,7 @@ public final class Distribution {
* package path.
*/
public static void init(final Context context) {
Distribution.init(context, context.getPackageResourcePath(), DEFAULT_PREFS);
Distribution.init(context, context.getPackageResourcePath(), null);
}
/**
@ -137,7 +135,7 @@ public final class Distribution {
}
public Distribution(final Context context) {
this(context, context.getPackageResourcePath(), DEFAULT_PREFS);
this(context, context.getPackageResourcePath(), null);
}
/**
@ -148,7 +146,13 @@ public final class Distribution {
private boolean doInit() {
// Bail if we've already tried to initialize the distribution, and
// there wasn't one.
SharedPreferences settings = context.getSharedPreferences(prefsBranch, Activity.MODE_PRIVATE);
final SharedPreferences settings;
if (prefsBranch == null) {
settings = GeckoSharedPrefs.forApp(context);
} else {
settings = context.getSharedPreferences(prefsBranch, Activity.MODE_PRIVATE);
}
String keyName = context.getPackageName() + ".distribution_state";
this.state = settings.getInt(keyName, STATE_UNKNOWN);
if (this.state == STATE_NONE) {

Просмотреть файл

@ -86,7 +86,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.MediaStore.Images.Media;
import android.telephony.CellLocation;
@ -156,7 +155,6 @@ public abstract class GeckoApp
public static final String PREFS_ALLOW_STATE_BUNDLE = "allowStateBundle";
public static final String PREFS_CRASHED = "crashed";
public static final String PREFS_NAME = "GeckoApp";
public static final String PREFS_OOM_EXCEPTION = "OOMException";
public static final String PREFS_VERSION_CODE = "versionCode";
public static final String PREFS_WAS_STOPPED = "wasStopped";
@ -177,7 +175,7 @@ public abstract class GeckoApp
private View mCameraView;
private OrientationEventListener mCameraOrientationEventListener;
public List<GeckoAppShell.AppStateListener> mAppStateListeners;
private static GeckoApp sAppContext;
private static volatile GeckoApp sAppContext;
protected MenuPanel mMenuPanel;
protected Menu mMenu;
protected GeckoProfile mProfile;
@ -245,7 +243,11 @@ public abstract class GeckoApp
}
public static SharedPreferences getAppSharedPreferences() {
return GeckoApp.sAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0);
if (sAppContext == null) {
return null;
}
return GeckoSharedPrefs.forApp(sAppContext);
}
public Activity getActivity() {
@ -1282,7 +1284,7 @@ public abstract class GeckoApp
// only intended to be used internally via Robocop, so a boolean
// is read from a private shared pref to prevent other apps from
// injecting states.
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences prefs = getAppSharedPreferences();
if (prefs.getBoolean(PREFS_ALLOW_STATE_BUNDLE, false)) {
Log.i(LOGTAG, "Restoring state from intent bundle");
prefs.edit().remove(PREFS_ALLOW_STATE_BUNDLE).commit();
@ -1768,8 +1770,7 @@ public abstract class GeckoApp
}
private String getSessionRestorePreference() {
return PreferenceManager.getDefaultSharedPreferences(this)
.getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
return getAppSharedPreferences().getString(GeckoPreferences.PREFS_RESTORE_SESSION, "quit");
}
private boolean getRestartFromIntent() {

Просмотреть файл

@ -30,6 +30,7 @@ public final class GeckoProfile {
// Used to "lock" the guest profile, so that we'll always restart in it
private static final String LOCK_FILE_NAME = ".active_lock";
public static final String DEFAULT_PROFILE = "default";
private static final String GUEST_PROFILE = "guest";
private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>();
private static String sDefaultProfileName = null;
@ -152,12 +153,21 @@ public final class GeckoProfile {
}
public static boolean removeProfile(Context context, String profileName) {
final boolean success;
try {
return new GeckoProfile(context, profileName).remove();
success = new GeckoProfile(context, profileName).remove();
} catch (NoMozillaDirectoryException e) {
Log.w(LOGTAG, "Unable to remove profile: no Mozilla directory.", e);
return true;
}
if (success) {
// Clear all shared prefs for the given profile.
GeckoSharedPrefs.forProfileName(context, profileName)
.edit().clear().commit();
}
return success;
}
public static GeckoProfile createGuestProfile(Context context) {
@ -193,7 +203,7 @@ public final class GeckoProfile {
if (sGuestProfile == null) {
File guestDir = getGuestDir(context);
if (guestDir.exists()) {
sGuestProfile = get(context, "guest", guestDir);
sGuestProfile = get(context, GUEST_PROFILE, guestDir);
sGuestProfile.mInGuestMode = true;
}
}
@ -218,14 +228,21 @@ public final class GeckoProfile {
}
private static void removeGuestProfile(Context context) {
boolean success = false;
try {
File guestDir = getGuestDir(context);
if (guestDir.exists()) {
delete(guestDir);
success = delete(guestDir);
}
} catch (Exception ex) {
Log.e(LOGTAG, "Error removing guest profile", ex);
}
if (success) {
// Clear all shared prefs for the guest profile.
GeckoSharedPrefs.forProfileName(context, GUEST_PROFILE)
.edit().clear().commit();
}
}
public static boolean delete(File file) throws IOException {

Просмотреть файл

@ -0,0 +1,284 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import org.mozilla.gecko.mozglue.RobocopTarget;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* {@code GeckoSharedPrefs} provides scoped SharedPreferences instances.
* You should use this API instead of using Context.getSharedPreferences()
* directly. There are three methods to get scoped SharedPreferences instances:
*
* forApp()
* Use it for app-wide, cross-profile pref keys.
* forProfile()
* Use it to fetch and store keys for the current profile.
* forProfileName()
* Use it to fetch and store keys from/for a specific profile.
*
* {@code GeckoSharedPrefs} has a notion of migrations. Migrations can used to
* migrate keys from one scope to another. You can trigger a new migration by
* incrementing PREFS_VERSION and updating migrateIfNecessary() accordingly.
*
* Migration history:
* 1: Move all PreferenceManager keys to app/profile scopes
*/
@RobocopTarget
public final class GeckoSharedPrefs {
private static final String LOGTAG = "GeckoSharedPrefs";
// Increment it to trigger a new migration
public static final int PREFS_VERSION = 1;
// Name for app-scoped prefs
public static final String APP_PREFS_NAME = "GeckoApp";
// The prefs key that holds the current migration
private static final String PREFS_VERSION_KEY = "gecko_shared_prefs_migration";
// For disabling migration when getting a SharedPreferences instance
private static final EnumSet<Flags> disableMigrations = EnumSet.of(Flags.DISABLE_MIGRATIONS);
// Timeout for migration commits to be done (10 seconds)
private static final int MIGRATION_COMMIT_TIMEOUT_MSEC = 10000;
// The keys that have to be moved from ProfileManager's default
// shared prefs to the profile from version 0 to 1.
private static final String[] PROFILE_MIGRATIONS_0_TO_1 = {
"home_panels",
"home_locale"
};
// For optimizing the migration check in subsequent get() calls
private static volatile boolean migrationDone = false;
public enum Flags {
DISABLE_MIGRATIONS
}
// Used when fetching profile-scoped prefs.
private static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-";
public static SharedPreferences forApp(Context context) {
return forApp(context, EnumSet.noneOf(Flags.class));
}
/**
* Returns an app-scoped SharedPreferences instance. You can disable
* migrations by using the DISABLE_MIGRATIONS flag.
*/
public static SharedPreferences forApp(Context context, EnumSet<Flags> flags) {
if (flags != null && !flags.contains(Flags.DISABLE_MIGRATIONS)) {
migrateIfNecessary(context);
}
return context.getSharedPreferences(APP_PREFS_NAME, 0);
}
public static SharedPreferences forProfile(Context context) {
return forProfile(context, EnumSet.noneOf(Flags.class));
}
/**
* Returns a SharedPreferences instance scoped to the current profile
* in the app. You can disable migrations by using the DISABLE_MIGRATIONS
* flag.
*/
public static SharedPreferences forProfile(Context context, EnumSet<Flags> flags) {
String profileName = GeckoProfile.get(context).getName();
if (profileName == null) {
throw new IllegalStateException("Could not get current profile name");
}
return forProfileName(context, profileName, flags);
}
public static SharedPreferences forProfileName(Context context, String profileName) {
return forProfileName(context, profileName, EnumSet.noneOf(Flags.class));
}
/**
* Returns an SharedPreferences instance scoped to the given profile name.
* You can disable migrations by using the DISABLE_MIGRATION flag.
*/
public static SharedPreferences forProfileName(Context context, String profileName,
EnumSet<Flags> flags) {
if (flags != null && !flags.contains(Flags.DISABLE_MIGRATIONS)) {
migrateIfNecessary(context);
}
final String prefsName = PROFILE_PREFS_NAME_PREFIX + profileName;
return context.getSharedPreferences(prefsName, 0);
}
/**
* Returns the current version of the prefs.
*/
public static int getVersion(Context context) {
return forApp(context, disableMigrations).getInt(PREFS_VERSION_KEY, 0);
}
/**
* Resets migration flag. Should only be used in tests.
*/
public static synchronized void reset() {
migrationDone = false;
}
/**
* Performs all prefs migrations in the background thread to avoid StrictMode
* exceptions from reading/writing in the UI thread. This method will block
* the current thread until the migration is finished.
*/
private static synchronized void migrateIfNecessary(final Context context) {
if (migrationDone) {
return;
}
if (ThreadUtils.isOnBackgroundThread()) {
Log.d(LOGTAG, "Already in background thread, migrating directly");
performMigration(context);
} else {
Log.d(LOGTAG, "Not in background thread, migrating with lock");
final Object migrationLock = new Object();
ThreadUtils.getBackgroundHandler().post(new Runnable() {
@Override
public void run() {
synchronized(migrationLock) {
performMigration(context);
migrationLock.notifyAll();
}
}
});
try {
synchronized(migrationLock) {
migrationLock.wait(MIGRATION_COMMIT_TIMEOUT_MSEC);
}
} catch (InterruptedException e) {
throw new IllegalStateException("Failed to commit migration before timeout");
}
}
migrationDone = true;
}
private static void performMigration(Context context) {
final SharedPreferences appPrefs = forApp(context, disableMigrations);
final int currentVersion = appPrefs.getInt(PREFS_VERSION_KEY, 0);
Log.d(LOGTAG, "Current version = " + currentVersion + ", prefs version = " + PREFS_VERSION);
if (currentVersion == PREFS_VERSION) {
return;
}
Log.d(LOGTAG, "Performing migration");
final Editor appEditor = appPrefs.edit();
// The migration always moves prefs to the default profile, not
// the current one. We might have to revisit this if we ever support
// multiple profiles.
final String defaultProfileName;
try {
defaultProfileName = GeckoProfile.getDefaultProfileName(context);
} catch (Exception e) {
throw new IllegalStateException("Failed to get default profile name for migration");
}
final Editor profileEditor = forProfileName(context, defaultProfileName, disableMigrations).edit();
List<String> profileKeys;
Editor pmEditor = null;
for (int v = currentVersion + 1; v <= PREFS_VERSION; v++) {
Log.d(LOGTAG, "Migrating to version = " + v);
switch (v) {
case 1:
profileKeys = Arrays.asList(PROFILE_MIGRATIONS_0_TO_1);
pmEditor = migrateFromPreferenceManager(context, appEditor, profileEditor, profileKeys);
break;
}
}
// Update prefs version accordingly.
appEditor.putInt(PREFS_VERSION_KEY, PREFS_VERSION);
appEditor.commit();
profileEditor.commit();
if (pmEditor != null) {
pmEditor.commit();
}
Log.d(LOGTAG, "All keys have been migrated");
}
/**
* Moves all preferences stored in PreferenceManager's default prefs
* to either app or profile scopes. The profile-scoped keys are defined
* in given profileKeys list, all other keys are moved to the app scope.
*/
public static Editor migrateFromPreferenceManager(Context context, Editor appEditor,
Editor profileEditor, List<String> profileKeys) {
Log.d(LOGTAG, "Migrating from PreferenceManager");
final SharedPreferences pmPrefs =
PreferenceManager.getDefaultSharedPreferences(context);
for (Map.Entry<String, ?> entry : pmPrefs.getAll().entrySet()) {
final String key = entry.getKey();
final Editor to;
if (profileKeys.contains(key)) {
to = profileEditor;
} else {
to = appEditor;
}
putEntry(to, key, entry.getValue());
}
// Clear PreferenceManager's prefs once we're done
// and return the Editor to be committed.
return pmPrefs.edit().clear();
}
@SuppressWarnings("unchecked")
private static void putEntry(Editor to, String key, Object value) {
Log.d(LOGTAG, "Migrating key = " + key + " with value = " + value);
if (value instanceof String) {
to.putString(key, (String) value);
} else if (value instanceof Boolean) {
to.putBoolean(key, (Boolean) value);
} else if (value instanceof Long) {
to.putLong(key, (Long) value);
} else if (value instanceof Float) {
to.putFloat(key, (Float) value);
} else if (value instanceof Integer) {
to.putInt(key, (Integer) value);
} else {
throw new IllegalStateException("Unrecognized value type for key: " + key);
}
}
}

Просмотреть файл

@ -14,7 +14,6 @@ import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import java.util.Map;
@ -63,7 +62,7 @@ public final class SharedPreferencesHelper
private SharedPreferences getSharedPreferences(String branch) {
if (branch == null) {
return PreferenceManager.getDefaultSharedPreferences(mContext);
return GeckoSharedPrefs.forApp(mContext);
} else {
return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE);
}

Просмотреть файл

@ -15,6 +15,7 @@ import java.util.Locale;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend;
import org.mozilla.gecko.home.HomeConfig.OnReloadListener;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
@ -28,7 +29,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
@ -50,7 +50,7 @@ class HomeConfigPrefsBackend implements HomeConfigBackend {
}
private SharedPreferences getSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(mContext);
return GeckoSharedPrefs.forProfile(mContext);
}
private State loadDefaultConfig() {

Просмотреть файл

@ -8,9 +8,10 @@ package org.mozilla.gecko.home;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.preference.PreferenceManager;
import android.util.Log;
import org.mozilla.gecko.GeckoSharedPrefs;
/**
* Cache used to store authentication state of dynamic panels. The values
* in this cache are set in JS through the Home.panels API.
@ -38,7 +39,7 @@ class PanelAuthCache {
}
private SharedPreferences getSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(mContext);
return GeckoSharedPrefs.forProfile(mContext);
}
private String getPanelAuthKey(String panelId) {

Просмотреть файл

@ -172,6 +172,7 @@ gbjar.sources += [
'GeckoProfileDirectories.java',
'GeckoProfilesProvider.java',
'GeckoScreenOrientation.java',
'GeckoSharedPrefs.java',
'GeckoSmsManager.java',
'GeckoThread.java',
'GeckoUpdateReceiver.java',

Просмотреть файл

@ -17,6 +17,7 @@ import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
@ -45,7 +46,6 @@ import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.TwoStatePreference;
import android.text.Editable;
@ -502,7 +502,7 @@ public class GeckoPreferences
final boolean value) {
final Intent intent = new Intent(action)
.putExtra("pref", pref)
.putExtra("branch", GeckoApp.PREFS_NAME)
.putExtra("branch", GeckoSharedPrefs.APP_PREFS_NAME)
.putExtra("enabled", value);
broadcastAction(context, intent);
}
@ -577,7 +577,7 @@ public class GeckoPreferences
* @return the value of the preference, or the default.
*/
public static boolean getBooleanPref(final Context context, final String name, boolean def) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
return prefs.getBoolean(name, def);
}

Просмотреть файл

@ -6,6 +6,7 @@
package org.mozilla.gecko.preferences;
import org.mozilla.gecko.R;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.util.ThreadUtils;
import android.app.AlertDialog;
@ -14,7 +15,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.widget.Button;
@ -205,8 +205,8 @@ class MultiChoicePreference extends DialogPreference {
return true;
}
PreferenceManager.getDefaultSharedPreferences(getContext())
.edit().putBoolean(key, value).commit();
GeckoSharedPrefs.forApp(getContext())
.edit().putBoolean(key, value).commit();
return true;
}
return false;
@ -216,8 +216,8 @@ class MultiChoicePreference extends DialogPreference {
if (!isPersistent())
return defaultReturnValue;
return PreferenceManager.getDefaultSharedPreferences(getContext())
.getBoolean(key, defaultReturnValue);
return GeckoSharedPrefs.forApp(getContext())
.getBoolean(key, defaultReturnValue);
}
/**

Просмотреть файл

@ -11,6 +11,7 @@ jar.sources += [
'src/harness/BrowserInstrumentationTestRunner.java',
'src/harness/BrowserTestListener.java',
'src/tests/BrowserTestCase.java',
'src/tests/TestGeckoSharedPrefs.java',
'src/tests/TestJarReader.java',
]
jar.generated_sources = [] # None yet -- try to keep it this way.

Просмотреть файл

@ -0,0 +1,155 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.browser.tests;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.HashSet;
import java.util.Set;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.GeckoSharedPrefs.Flags;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.preference.PreferenceManager;
import android.test.RenamingDelegatingContext;
/**
* Test GeckoSharedPrefs migrations.
*/
public class TestGeckoSharedPrefs extends BrowserTestCase {
private static class TestContext extends RenamingDelegatingContext {
private static final String PREFIX = "TestGeckoSharedPrefs-";
private final Set<String> usedPrefs;
public TestContext(Context context) {
super(context, PREFIX);
usedPrefs = Collections.synchronizedSet(new HashSet<String>());
}
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
usedPrefs.add(name);
return super.getSharedPreferences(PREFIX + name, mode);
}
public void clearUsedPrefs() {
for (String prefsName : usedPrefs) {
getSharedPreferences(prefsName, 0).edit().clear().commit();
}
usedPrefs.clear();
}
}
private static final EnumSet<Flags> disableMigrations = EnumSet.of(Flags.DISABLE_MIGRATIONS);
private TestContext context;
protected void setUp() {
context = new TestContext(getApplicationContext());
}
protected void tearDown() {
context.clearUsedPrefs();
GeckoSharedPrefs.reset();
}
public void testDisableMigrations() {
// Version is 0 before any migration
assertEquals(0, GeckoSharedPrefs.getVersion(context));
// Get prefs with migrations disabled
GeckoSharedPrefs.forApp(context, disableMigrations);
GeckoSharedPrefs.forProfile(context, disableMigrations);
GeckoSharedPrefs.forProfileName(context, "someProfile", disableMigrations);
// Version should still be 0
assertEquals(0, GeckoSharedPrefs.getVersion(context));
}
public void testPrefsVersion() {
// Version is 0 before any migration
assertEquals(0, GeckoSharedPrefs.getVersion(context));
// Trigger migration by getting a SharedPreferences instance
GeckoSharedPrefs.forApp(context);
// Version should be current after migration
assertEquals(GeckoSharedPrefs.PREFS_VERSION, GeckoSharedPrefs.getVersion(context));
}
public void testMigrateFromPreferenceManager() {
SharedPreferences appPrefs = GeckoSharedPrefs.forApp(context, disableMigrations);
assertTrue(appPrefs.getAll().isEmpty());
final Editor appEditor = appPrefs.edit();
SharedPreferences profilePrefs = GeckoSharedPrefs.forProfileName(context, GeckoProfile.DEFAULT_PROFILE, disableMigrations);
assertTrue(profilePrefs.getAll().isEmpty());
final Editor profileEditor = profilePrefs.edit();
final SharedPreferences pmPrefs = PreferenceManager.getDefaultSharedPreferences(context);
assertTrue(pmPrefs.getAll().isEmpty());
Editor pmEditor = pmPrefs.edit();
// Insert a key for each type to exercise the
// migration path a bit more thoroughly.
pmEditor.putInt("int_key", 23);
pmEditor.putLong("long_key", 23L);
pmEditor.putString("string_key", "23");
pmEditor.putFloat("float_key", 23.3f);
final String[] profileKeys = {
"string_profile",
"int_profile"
};
// Insert keys that are expected to be moved to the
// PROFILE scope.
pmEditor.putString(profileKeys[0], "24");
pmEditor.putInt(profileKeys[1], 24);
// Commit changes to PreferenceManager
pmEditor.commit();
assertEquals(6, pmPrefs.getAll().size());
// Perform actual migration with the given editors
pmEditor = GeckoSharedPrefs.migrateFromPreferenceManager(context, appEditor,
profileEditor, Arrays.asList(profileKeys));
// Commit changes applied during the migration
appEditor.commit();
profileEditor.commit();
pmEditor.commit();
// PreferenceManager should have no keys
assertTrue(pmPrefs.getAll().isEmpty());
// App should have all keys except the profile ones
assertEquals(4, appPrefs.getAll().size());
// Ensure app scope doesn't have any of the profile keys
for (int i = 0; i < profileKeys.length; i++) {
assertFalse(appPrefs.contains(profileKeys[i]));
}
// Check app keys
assertEquals(23, appPrefs.getInt("int_key", 0));
assertEquals(23L, appPrefs.getLong("long_key", 0L));
assertEquals("23", appPrefs.getString("string_key", ""));
assertEquals(23.3f, appPrefs.getFloat("float_key", 0));
assertEquals(2, profilePrefs.getAll().size());
assertEquals("24", profilePrefs.getString(profileKeys[0], ""));
assertEquals(24, profilePrefs.getInt(profileKeys[1], 0));
}
}