зеркало из https://github.com/mozilla/gecko-dev.git
Bug 940575 - Implement scopes for SharedPreferences (r=rnewman)
This commit is contained in:
Родитель
dc23abbc03
Коммит
f1fc78b9e8
|
@ -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));
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче