Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc2c128b authored by Hyunyoung Song's avatar Hyunyoung Song Committed by Android (Google) Code Review
Browse files

Merge "Updating feature flags subclassing" into ub-launcher3-master

parents 8c786b2e 94fa76fb
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -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>(...);
}
+19 −8
Original line number Diff line number Diff line
@@ -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);
    }
}
+0 −117
Original line number Diff line number Diff line
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);
            }
        }
    }
}
+0 −41
Original line number Diff line number Diff line
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());
    }
}
+8 −3
Original line number Diff line number Diff line
@@ -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;
    }
}
Loading