Merge "Updating feature flags subclassing" into ub-launcher3-master
This commit is contained in:
commit
dc2c128b20
|
@ -2,12 +2,6 @@
|
|||
*;
|
||||
}
|
||||
|
||||
# Proguard will strip new callbacks in LauncherApps.Callback from
|
||||
# WrappedCallback if compiled against an older SDK. Don't let this happen.
|
||||
-keep class com.android.launcher3.compat.** {
|
||||
*;
|
||||
}
|
||||
|
||||
-keep class com.android.launcher3.graphics.ShadowDrawable {
|
||||
public <init>(...);
|
||||
}
|
||||
|
|
|
@ -16,22 +16,28 @@
|
|||
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
|
||||
import com.android.launcher3.config.FeatureFlags.DebugFlag;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public class DeviceFlag extends DebugFlag {
|
||||
|
||||
public class TogglableFlag extends BaseTogglableFlag {
|
||||
public static final String NAMESPACE_LAUNCHER = "launcher";
|
||||
public static final String TAG = "TogglableFlag";
|
||||
|
||||
public TogglableFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, defaultValue, description);
|
||||
private final boolean mDefaultValueInCode;
|
||||
|
||||
public DeviceFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, getDeviceValue(key, defaultValue), description);
|
||||
mDefaultValueInCode = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOverridenDefaultValue(boolean value) {
|
||||
return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value);
|
||||
protected StringBuilder appendProps(StringBuilder src) {
|
||||
return super.appendProps(src).append(", mDefaultValueInCode=").append(mDefaultValueInCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,12 +45,17 @@ public class TogglableFlag extends BaseTogglableFlag {
|
|||
DeviceConfig.addOnPropertiesChangedListener(
|
||||
NAMESPACE_LAUNCHER,
|
||||
context.getMainExecutor(),
|
||||
(properties) -> {
|
||||
properties -> {
|
||||
if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
|
||||
return;
|
||||
}
|
||||
defaultValue = getDeviceValue(key, mDefaultValueInCode);
|
||||
initialize(context);
|
||||
r.run();
|
||||
});
|
||||
}
|
||||
|
||||
protected static boolean getDeviceValue(String key, boolean defaultValue) {
|
||||
return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValue);
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package com.android.launcher3.config;
|
||||
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
|
||||
import com.android.launcher3.uioverrides.TogglableFlag;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Test rule that makes overriding flags in Robolectric tests easier. This rule clears all flags
|
||||
* before and after your test, avoiding one test method affecting subsequent methods.
|
||||
*
|
||||
* <p>Usage:
|
||||
* <pre>
|
||||
* {@literal @}Rule public final FlagOverrideRule flags = new FlagOverrideRule();
|
||||
*
|
||||
* {@literal @}FlagOverride(flag = "FOO", value=true)
|
||||
* {@literal @}Test public void myTest() {
|
||||
* ...
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public final class FlagOverrideRule implements TestRule {
|
||||
|
||||
private final HashMap<String, Boolean> mDefaultOverrides = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Container annotation for handling multiple {@link FlagOverride} annotations.
|
||||
* <p>
|
||||
* <p>Don't use this directly, use repeated {@link FlagOverride} annotations instead.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface FlagOverrides {
|
||||
FlagOverride[] value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@Repeatable(FlagOverrides.class)
|
||||
public @interface FlagOverride {
|
||||
String key();
|
||||
|
||||
boolean value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new MyStatement(base, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default override to apply on all tests
|
||||
*/
|
||||
public FlagOverrideRule setOverride(BaseTogglableFlag flag, boolean value) {
|
||||
mDefaultOverrides.put(flag.getKey(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private class MyStatement extends Statement {
|
||||
|
||||
private final Statement mBase;
|
||||
private final Description mDescription;
|
||||
|
||||
|
||||
MyStatement(Statement base, Description description) {
|
||||
mBase = base;
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
Map<String, BaseTogglableFlag> allFlags = FeatureFlags.getTogglableFlags().stream()
|
||||
.collect(Collectors.toMap(TogglableFlag::getKey, Function.identity()));
|
||||
|
||||
HashMap<BaseTogglableFlag, Boolean> changedValues = new HashMap<>();
|
||||
FlagOverride[] overrides = new FlagOverride[0];
|
||||
try {
|
||||
for (Annotation annotation : mDescription.getAnnotations()) {
|
||||
if (annotation.annotationType() == FlagOverride.class) {
|
||||
overrides = new FlagOverride[] { (FlagOverride) annotation };
|
||||
} else if (annotation.annotationType() == FlagOverrides.class) {
|
||||
// Note: this branch is hit if the annotation is repeated
|
||||
overrides = ((FlagOverrides) annotation).value();
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<String, Boolean> allOverrides = new HashMap<>(mDefaultOverrides);
|
||||
Arrays.stream(overrides).forEach(o -> allOverrides.put(o.key(), o.value()));
|
||||
|
||||
allOverrides.forEach((key, val) -> {
|
||||
BaseTogglableFlag flag = allFlags.get(key);
|
||||
changedValues.put(flag, flag.get());
|
||||
flag.setForTests(val);
|
||||
});
|
||||
mBase.evaluate();
|
||||
} finally {
|
||||
// Clear the values
|
||||
changedValues.forEach(BaseTogglableFlag::setForTests);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package com.android.launcher3.config;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
|
||||
import com.android.launcher3.util.LauncherRoboTestRunner;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Sample Robolectric test that demonstrates flag-overriding.
|
||||
*/
|
||||
@RunWith(LauncherRoboTestRunner.class)
|
||||
public class FlagOverrideSampleTest {
|
||||
|
||||
// Check out https://junit.org/junit4/javadoc/4.12/org/junit/Rule.html for more information
|
||||
// on @Rules.
|
||||
@Rule
|
||||
public final FlagOverrideRule flags = new FlagOverrideRule();
|
||||
|
||||
/**
|
||||
* Test if flag can be overriden to true via annoation.
|
||||
*/
|
||||
@FlagOverride(key = "FAKE_LANDSCAPE_UI", value = true)
|
||||
@Test
|
||||
public void withFlagOn() {
|
||||
assertTrue(FeatureFlags.FAKE_LANDSCAPE_UI.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if flag can be overriden to false via annoation.
|
||||
*/
|
||||
@FlagOverride(key = "FAKE_LANDSCAPE_UI", value = false)
|
||||
@Test
|
||||
public void withFlagOff() {
|
||||
assertFalse(FeatureFlags.FAKE_LANDSCAPE_UI.get());
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ package com.android.launcher3.shadows;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.launcher3.uioverrides.TogglableFlag;
|
||||
import com.android.launcher3.uioverrides.DeviceFlag;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
|
@ -27,12 +27,17 @@ import org.robolectric.annotation.Implements;
|
|||
/**
|
||||
* Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
|
||||
*/
|
||||
@Implements(value = TogglableFlag.class, isInAndroidSdk = false)
|
||||
public class ShadowTogglableFlag {
|
||||
@Implements(value = DeviceFlag.class, isInAndroidSdk = false)
|
||||
public class ShadowDeviceFlag {
|
||||
|
||||
/**
|
||||
* Mock change listener as it uses internal system classes not available to robolectric
|
||||
*/
|
||||
@Implementation
|
||||
protected void addChangeListener(Context context, Runnable r) { }
|
||||
|
||||
@Implementation
|
||||
protected static boolean getDeviceValue(String key, boolean defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ import com.android.launcher3.shadows.LShadowLauncherApps;
|
|||
import com.android.launcher3.shadows.LShadowUserManager;
|
||||
import com.android.launcher3.shadows.ShadowLooperExecutor;
|
||||
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
|
||||
import com.android.launcher3.shadows.ShadowTogglableFlag;
|
||||
import com.android.launcher3.shadows.ShadowDeviceFlag;
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
|
||||
import org.junit.runners.model.InitializationError;
|
||||
|
@ -50,7 +50,7 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner {
|
|||
|
||||
ShadowLooperExecutor.class,
|
||||
ShadowMainThreadInitializedObject.class,
|
||||
ShadowTogglableFlag.class,
|
||||
ShadowDeviceFlag.class,
|
||||
};
|
||||
|
||||
public LauncherRoboTestRunner(Class<?> testClass) throws InitializationError {
|
||||
|
|
|
@ -16,37 +16,25 @@
|
|||
|
||||
package com.android.launcher3.config;
|
||||
|
||||
import static androidx.core.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.BuildConfig;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.uioverrides.TogglableFlag;
|
||||
import com.android.launcher3.uioverrides.DeviceFlag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Defines a set of flags used to control various launcher behaviors.
|
||||
*
|
||||
* <p>All the flags should be defined here with appropriate default values.
|
||||
*/
|
||||
@Keep
|
||||
public final class FeatureFlags {
|
||||
|
||||
private static final Object sLock = new Object();
|
||||
@GuardedBy("sLock")
|
||||
private static final List<TogglableFlag> sFlags = new ArrayList<>();
|
||||
private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
|
||||
|
||||
static final String FLAGS_PREF_NAME = "featureFlags";
|
||||
public static final String FLAGS_PREF_NAME = "featureFlags";
|
||||
|
||||
private FeatureFlags() { }
|
||||
|
||||
|
@ -62,7 +50,6 @@ public final class FeatureFlags {
|
|||
*/
|
||||
public static final boolean QSB_ON_FIRST_SCREEN = true;
|
||||
|
||||
|
||||
/**
|
||||
* Feature flag to handle define config changes dynamically instead of killing the process.
|
||||
*
|
||||
|
@ -73,70 +60,66 @@ public final class FeatureFlags {
|
|||
* and set a default value for the flag. This will be the default value on Debug builds.
|
||||
*/
|
||||
// When enabled the promise icon is visible in all apps while installation an app.
|
||||
public static final TogglableFlag PROMISE_APPS_IN_ALL_APPS = new TogglableFlag(
|
||||
public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(
|
||||
"PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps");
|
||||
|
||||
// When enabled a promise icon is added to the home screen when install session is active.
|
||||
public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS =
|
||||
new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true,
|
||||
"Adds a promise icon to the home screen for new install sessions.");
|
||||
public static final BooleanFlag PROMISE_APPS_NEW_INSTALLS = getDebugFlag(
|
||||
"PROMISE_APPS_NEW_INSTALLS", true,
|
||||
"Adds a promise icon to the home screen for new install sessions.");
|
||||
|
||||
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
|
||||
public static final BooleanFlag APPLY_CONFIG_AT_RUNTIME = getDebugFlag(
|
||||
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
|
||||
|
||||
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
|
||||
true, "Enable springs for quickstep animations");
|
||||
public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
|
||||
"QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
|
||||
|
||||
public static final TogglableFlag UNSTABLE_SPRINGS = new TogglableFlag("UNSTABLE_SPRINGS",
|
||||
false, "Enable unstable springs for quickstep animations");
|
||||
public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
|
||||
"UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
|
||||
|
||||
public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
|
||||
"ADAPTIVE_ICON_WINDOW_ANIM", true,
|
||||
"Use adaptive icons for window animations.");
|
||||
public static final BooleanFlag ADAPTIVE_ICON_WINDOW_ANIM = getDebugFlag(
|
||||
"ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations.");
|
||||
|
||||
public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
|
||||
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
|
||||
|
||||
public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
|
||||
"ENABLE_HINTS_IN_OVERVIEW", false,
|
||||
"Show chip hints and gleams on the overview screen");
|
||||
public static final BooleanFlag ENABLE_HINTS_IN_OVERVIEW = getDebugFlag(
|
||||
"ENABLE_HINTS_IN_OVERVIEW", false, "Show chip hints and gleams on the overview screen");
|
||||
|
||||
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
|
||||
"FAKE_LANDSCAPE_UI", false,
|
||||
"Rotate launcher UI instead of using transposed layout");
|
||||
public static final BooleanFlag FAKE_LANDSCAPE_UI = getDebugFlag(
|
||||
"FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
|
||||
|
||||
public static final TogglableFlag FOLDER_NAME_SUGGEST = new TogglableFlag(
|
||||
"FOLDER_NAME_SUGGEST", true,
|
||||
"Suggests folder names instead of blank text.");
|
||||
public static final BooleanFlag FOLDER_NAME_SUGGEST = getDebugFlag(
|
||||
"FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text.");
|
||||
|
||||
public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag(
|
||||
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
|
||||
"APP_SEARCH_IMPROVEMENTS", false,
|
||||
"Adds localized title and keyword search and ranking");
|
||||
|
||||
public static final TogglableFlag ENABLE_PREDICTION_DISMISS = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
|
||||
"ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
|
||||
|
||||
public static final TogglableFlag ENABLE_QUICK_CAPTURE_GESTURE = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
|
||||
"ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
|
||||
|
||||
public static final TogglableFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = new TogglableFlag(
|
||||
public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
|
||||
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
|
||||
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
|
||||
|
||||
public static final TogglableFlag ENABLE_HYBRID_HOTSEAT = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
|
||||
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
|
||||
|
||||
public static final TogglableFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
|
||||
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
|
||||
|
||||
public static final TogglableFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
|
||||
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false,
|
||||
"Show launcher preview in grid picker");
|
||||
|
||||
public static final TogglableFlag ENABLE_OVERVIEW_ACTIONS = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
|
||||
"ENABLE_OVERVIEW_ACTIONS", false, "Show app actions in Overview");
|
||||
|
||||
public static final TogglableFlag ENABLE_DATABASE_RESTORE = new TogglableFlag(
|
||||
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
|
||||
"ENABLE_DATABASE_RESTORE", true,
|
||||
"Enable database restore when new restore session is created");
|
||||
|
||||
|
@ -145,126 +128,81 @@ public final class FeatureFlags {
|
|||
"Replace Smartspace with a version rendered by System UI.");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
// Avoid the disk read for user builds
|
||||
if (Utilities.IS_DEBUG_DEVICE) {
|
||||
synchronized (sLock) {
|
||||
for (BaseTogglableFlag flag : sFlags) {
|
||||
flag.initialize(context);
|
||||
}
|
||||
synchronized (sDebugFlags) {
|
||||
for (DebugFlag flag : sDebugFlags) {
|
||||
flag.initialize(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<TogglableFlag> getTogglableFlags() {
|
||||
// By Java Language Spec 12.4.2
|
||||
// https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2, the
|
||||
// TogglableFlag instances on FeatureFlags will be created before those on the FeatureFlags
|
||||
// subclass. This code handles flags that are redeclared in FeatureFlags, ensuring the
|
||||
// FeatureFlags one takes priority.
|
||||
SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
|
||||
synchronized (sLock) {
|
||||
for (TogglableFlag flag : sFlags) {
|
||||
flagsByKey.put(flag.getKey(), flag);
|
||||
}
|
||||
static List<DebugFlag> getDebugFlags() {
|
||||
synchronized (sDebugFlags) {
|
||||
return new ArrayList<>(sDebugFlags);
|
||||
}
|
||||
return new ArrayList<>(flagsByKey.values());
|
||||
}
|
||||
|
||||
public static abstract class BaseTogglableFlag {
|
||||
private final String key;
|
||||
// should be value that is hardcoded in client side.
|
||||
// Comparatively, getDefaultValue() can be overridden.
|
||||
private final boolean defaultValue;
|
||||
private final String description;
|
||||
private boolean currentValue;
|
||||
public static class BooleanFlag {
|
||||
|
||||
public BaseTogglableFlag(
|
||||
String key,
|
||||
boolean defaultValue,
|
||||
String description) {
|
||||
this.key = checkNotNull(key);
|
||||
this.currentValue = this.defaultValue = defaultValue;
|
||||
this.description = checkNotNull(description);
|
||||
public final String key;
|
||||
public boolean defaultValue;
|
||||
|
||||
synchronized (sLock) {
|
||||
sFlags.add((TogglableFlag)this);
|
||||
}
|
||||
public BooleanFlag(String key, boolean defaultValue) {
|
||||
this.key = key;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/** Set the value of this flag. This should only be used in tests. */
|
||||
@VisibleForTesting
|
||||
void setForTests(boolean value) {
|
||||
currentValue = value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
protected void initialize(Context context) {
|
||||
currentValue = getFromStorage(context, getDefaultValue());
|
||||
}
|
||||
|
||||
protected abstract boolean getOverridenDefaultValue(boolean value);
|
||||
|
||||
protected abstract void addChangeListener(Context context, Runnable r);
|
||||
|
||||
public void updateStorage(Context context, boolean value) {
|
||||
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
|
||||
Context.MODE_PRIVATE).edit();
|
||||
if (value == getDefaultValue()) {
|
||||
editor.remove(key).apply();
|
||||
} else {
|
||||
editor.putBoolean(key, value).apply();
|
||||
}
|
||||
}
|
||||
|
||||
boolean getFromStorage(Context context, boolean defaultValue) {
|
||||
return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(key, getDefaultValue());
|
||||
}
|
||||
|
||||
boolean getDefaultValue() {
|
||||
return getOverridenDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
/** Returns the value of the flag at process start, including any overrides present. */
|
||||
public boolean get() {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
String getDescription() {
|
||||
return description;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TogglableFlag{"
|
||||
+ "key=" + key + ", "
|
||||
+ "defaultValue=" + defaultValue + ", "
|
||||
+ "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
|
||||
+ "currentValue=" + currentValue + ", "
|
||||
+ "description=" + description
|
||||
+ "}";
|
||||
return appendProps(new StringBuilder()
|
||||
.append(getClass().getSimpleName()).append('{'))
|
||||
.append('}').toString();
|
||||
}
|
||||
|
||||
protected StringBuilder appendProps(StringBuilder src) {
|
||||
return src.append("key=").append(key).append(", defaultValue=").append(defaultValue);
|
||||
}
|
||||
|
||||
public void addChangeListener(Context context, Runnable r) { }
|
||||
}
|
||||
|
||||
public static class DebugFlag extends BooleanFlag {
|
||||
|
||||
public final String description;
|
||||
private boolean mCurrentValue;
|
||||
|
||||
public DebugFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, defaultValue);
|
||||
this.description = description;
|
||||
mCurrentValue = this.defaultValue;
|
||||
synchronized (sDebugFlags) {
|
||||
sDebugFlags.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof TogglableFlag) {
|
||||
BaseTogglableFlag that = (BaseTogglableFlag) o;
|
||||
return (this.key.equals(that.getKey()))
|
||||
&& (this.getDefaultValue() == that.getDefaultValue())
|
||||
&& (this.description.equals(that.getDescription()));
|
||||
}
|
||||
return false;
|
||||
public boolean get() {
|
||||
return mCurrentValue;
|
||||
}
|
||||
|
||||
public void initialize(Context context) {
|
||||
mCurrentValue = context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return key.hashCode();
|
||||
protected StringBuilder appendProps(StringBuilder src) {
|
||||
return super.appendProps(src).append(", mCurrentValue=").append(mCurrentValue)
|
||||
.append(", description=").append(description);
|
||||
}
|
||||
}
|
||||
|
||||
private static BooleanFlag getDebugFlag(String key, boolean defaultValue, String description) {
|
||||
return Utilities.IS_DEBUG_DEVICE
|
||||
? new DebugFlag(key, defaultValue, description)
|
||||
: new BooleanFlag(key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.launcher3.config;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Process;
|
||||
|
@ -31,8 +33,7 @@ import androidx.preference.PreferenceGroup;
|
|||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
|
||||
import com.android.launcher3.uioverrides.TogglableFlag;
|
||||
import com.android.launcher3.config.FeatureFlags.DebugFlag;
|
||||
|
||||
/**
|
||||
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
|
||||
|
@ -49,23 +50,26 @@ public final class FlagTogglerPrefUi {
|
|||
|
||||
@Override
|
||||
public void putBoolean(String key, boolean value) {
|
||||
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
if (flag.getKey().equals(key)) {
|
||||
boolean prevValue = flag.get();
|
||||
flag.updateStorage(mContext, value);
|
||||
updateMenu();
|
||||
if (flag.get() != prevValue) {
|
||||
Toast.makeText(mContext, "Flag applied", Toast.LENGTH_SHORT).show();
|
||||
for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
|
||||
if (flag.key.equals(key)) {
|
||||
SharedPreferences.Editor editor = mContext.getSharedPreferences(
|
||||
FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit();
|
||||
if (value == flag.defaultValue) {
|
||||
editor.remove(key).apply();
|
||||
} else {
|
||||
editor.putBoolean(key, value).apply();
|
||||
}
|
||||
updateMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
if (flag.getKey().equals(key)) {
|
||||
return flag.getFromStorage(mContext, defaultValue);
|
||||
for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
|
||||
if (flag.key.equals(key)) {
|
||||
return mContext.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(key, flag.defaultValue);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
|
@ -76,7 +80,7 @@ public final class FlagTogglerPrefUi {
|
|||
mFragment = fragment;
|
||||
mContext = fragment.getActivity();
|
||||
mSharedPreferences = mContext.getSharedPreferences(
|
||||
FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
|
||||
FLAGS_PREF_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public void applyTo(PreferenceGroup parent) {
|
||||
|
@ -84,12 +88,12 @@ public final class FlagTogglerPrefUi {
|
|||
// flag with a different value than the default. That way, when we flip flags in
|
||||
// future, engineers will pick up the new value immediately. To accomplish this, we use a
|
||||
// custom preference data store.
|
||||
for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
|
||||
SwitchPreference switchPreference = new SwitchPreference(mContext);
|
||||
switchPreference.setKey(flag.getKey());
|
||||
switchPreference.setDefaultValue(flag.getDefaultValue());
|
||||
switchPreference.setKey(flag.key);
|
||||
switchPreference.setDefaultValue(flag.defaultValue);
|
||||
switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
|
||||
switchPreference.setTitle(flag.getKey());
|
||||
switchPreference.setTitle(flag.key);
|
||||
updateSummary(switchPreference, flag);
|
||||
switchPreference.setPreferenceDataStore(mDataStore);
|
||||
parent.addPreference(switchPreference);
|
||||
|
@ -100,11 +104,11 @@ public final class FlagTogglerPrefUi {
|
|||
/**
|
||||
* Updates the summary to show the description and whether the flag overrides the default value.
|
||||
*/
|
||||
private void updateSummary(SwitchPreference switchPreference, BaseTogglableFlag flag) {
|
||||
String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
|
||||
String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
|
||||
switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
|
||||
switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.getDescription()));
|
||||
private void updateSummary(SwitchPreference switchPreference, DebugFlag flag) {
|
||||
String onWarning = flag.defaultValue ? "" : "<b>OVERRIDDEN</b><br>";
|
||||
String offWarning = flag.defaultValue ? "<b>OVERRIDDEN</b><br>" : "";
|
||||
switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.description));
|
||||
switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.description));
|
||||
}
|
||||
|
||||
private void updateMenu() {
|
||||
|
@ -135,12 +139,12 @@ public final class FlagTogglerPrefUi {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean getFlagStateFromSharedPrefs(BaseTogglableFlag flag) {
|
||||
return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
|
||||
private boolean getFlagStateFromSharedPrefs(DebugFlag flag) {
|
||||
return mDataStore.getBoolean(flag.key, flag.defaultValue);
|
||||
}
|
||||
|
||||
private boolean anyChanged() {
|
||||
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
|
||||
for (DebugFlag flag : FeatureFlags.getDebugFlags()) {
|
||||
if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
public class FolderNameProvider {
|
||||
|
||||
private static final String TAG = FeatureFlags.FOLDER_NAME_SUGGEST.getKey();
|
||||
private static final String TAG = "FolderNameProvider";
|
||||
private static final boolean DEBUG = FeatureFlags.FOLDER_NAME_SUGGEST.get();
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,21 +16,11 @@
|
|||
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
import android.content.Context;
|
||||
import com.android.launcher3.config.FeatureFlags.DebugFlag;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag;
|
||||
public class DeviceFlag extends DebugFlag {
|
||||
|
||||
public class TogglableFlag extends BaseTogglableFlag {
|
||||
|
||||
public TogglableFlag(String key, boolean defaultValue, String description) {
|
||||
public DeviceFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, defaultValue, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOverridenDefaultValue(boolean value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChangeListener(Context context, Runnable r) { }
|
||||
}
|
Loading…
Reference in New Issue