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

Commit 7e5658cb authored by Dave Mankoff's avatar Dave Mankoff Committed by Android (Google) Code Review
Browse files

Merge "Remove FeatureFlagReader. Allow resource overlays."

parents ef6bc3d1 7bc36943
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -50,17 +50,6 @@ java_library {
    srcs: ["src/com/android/systemui/EventLogTags.logtags"],
}

java_library {
    name: "SystemUI-flags",
    srcs: [
        "src/com/android/systemui/flags/Flags.java",
    ],
    libs: [
        "SystemUI-flag-types",
    ],
    static_kotlin_stdlib: false,
}

filegroup {
    name: "ReleaseJavaFiles",
    srcs: [
@@ -127,7 +116,6 @@ android_library {
        "iconloader_base",
        "SystemUI-tags",
        "SystemUI-proto",
        "SystemUI-flags",
        "monet",
        "dagger2",
        "jsr330",
+0 −4
Original line number Diff line number Diff line
@@ -45,9 +45,6 @@ android_library {
        ":wm_shell-aidls",
        ":wm_shell_util-sources",
    ],
    libs: [
        "SystemUI-flags",
    ],
    static_libs: [
        "PluginCoreLib",
        "androidx.dynamicanimation_dynamicanimation",
@@ -75,7 +72,6 @@ java_library {
    ],
    static_kotlin_stdlib: false,
    libs: [
        "SystemUI-flags",
        "androidx.concurrent_concurrent-futures",
    ],
    static_libs: [
+17 −6
Original line number Diff line number Diff line
@@ -22,15 +22,21 @@ import android.os.Parcelable
interface Flag<T> : Parcelable {
    val id: Int
    val default: T
    val resourceOverride: Int

    override fun describeContents() = 0

    fun hasResourceOverride(): Boolean {
        return resourceOverride != -1
    }
}

// Consider using the "parcelize" kotlin library.

data class BooleanFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Boolean = false
    override val default: Boolean = false,
    override val resourceOverride: Int = -1
) : Flag<Boolean> {

    companion object {
@@ -54,7 +60,8 @@ data class BooleanFlag @JvmOverloads constructor(

data class StringFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: String = ""
    override val default: String = "",
    override val resourceOverride: Int = -1
) : Flag<String> {
    companion object {
        @JvmField
@@ -77,7 +84,8 @@ data class StringFlag @JvmOverloads constructor(

data class IntFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Int = 0
    override val default: Int = 0,
    override val resourceOverride: Int = -1
) : Flag<Int> {

    companion object {
@@ -101,7 +109,8 @@ data class IntFlag @JvmOverloads constructor(

data class LongFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Long = 0
    override val default: Long = 0,
    override val resourceOverride: Int = -1
) : Flag<Long> {

    companion object {
@@ -125,7 +134,8 @@ data class LongFlag @JvmOverloads constructor(

data class FloatFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Float = 0f
    override val default: Float = 0f,
    override val resourceOverride: Int = -1
) : Flag<Float> {

    companion object {
@@ -149,7 +159,8 @@ data class FloatFlag @JvmOverloads constructor(

data class DoubleFlag @JvmOverloads constructor(
    override val id: Int,
    override val default: Double = 0.0
    override val default: Double = 0.0,
    override val resourceOverride: Int = -1
) : Flag<Double> {

    companion object {
+0 −175
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.flags;

import android.content.res.Resources;
import android.util.SparseArray;

import androidx.annotation.BoolRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.wrapper.BuildInfo;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;

import javax.inject.Inject;
/**
 * Reads and caches feature flags for quick access
 *
 * Feature flags must be defined as boolean resources. For example:
 *
 * {@code
 *  <bool name="flag_foo_bar_baz">false</bool>
 * }
 *
 * It is strongly recommended that the name of the resource begin with "flag_".
 *
 * Flags can be overridden via adb on development builds. For example, to override the flag from the
 * previous example, do the following:
 *
 * {@code
 *  $ adb shell setprop persist.systemui.flag_foo_bar_baz 1
 * }
 *
 * Note that all storage keys begin with "flag_", even if their associated resId does not.
 *
 * Calls to this class should probably be wrapped by a method in {@link FeatureFlags}.
 */
@SysUISingleton
public class FeatureFlagReader implements Dumpable {
    private final Resources mResources;
    private final boolean mAreFlagsOverrideable;
    private final SystemPropertiesHelper mSystemPropertiesHelper;
    private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();

    private FlagReader mFlagReader;

    @Inject
    public FeatureFlagReader(
            @Main Resources resources,
            BuildInfo build,
            DumpManager dumpManager,
            SystemPropertiesHelper systemPropertiesHelper,
            FlagReader reader) {
        mResources = resources;
        mFlagReader = reader;
        mSystemPropertiesHelper = systemPropertiesHelper;
        mAreFlagsOverrideable =
                build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
        dumpManager.registerDumpable("FeatureFlags", this);
    }

    boolean isEnabled(BooleanFlag flag) {
        return mFlagReader.isEnabled(flag.getId(), flag.getDefault());
    }

    void addListener(FlagReader.Listener listener) {
        mFlagReader.addListener(listener);
    }

    void removeListener(FlagReader.Listener listener) {
        mFlagReader.removeListener(listener);
    }

    /**
     * Returns true if the specified feature flag has been enabled.
     *
     * @param resId The backing boolean resource that determines the value of the flag. This value
     *              can be overridden via DeviceConfig on development builds.
     */
    public boolean isEnabled(@BoolRes int resId) {
        synchronized (mCachedFlags) {
            CachedFlag cachedFlag = mCachedFlags.get(resId);

            if (cachedFlag == null) {
                String name = resourceIdToFlagName(resId);
                boolean value = mResources.getBoolean(resId);
                if (mAreFlagsOverrideable) {
                    value = mSystemPropertiesHelper.getBoolean(flagNameToStorageKey(name), value);
                }

                cachedFlag = new CachedFlag(name, value);
                mCachedFlags.put(resId, cachedFlag);
            }

            return cachedFlag.value;
        }
    }

    private String resourceIdToFlagName(@BoolRes int resId) {
        String resName = mResources.getResourceEntryName(resId);
        if (resName.startsWith(RESNAME_PREFIX)) {
            resName = resName.substring(RESNAME_PREFIX.length());
        }
        return resName;
    }

    private String flagNameToStorageKey(String flagName) {
        if (flagName.startsWith(STORAGE_KEY_PREFIX)) {
            return flagName;
        } else {
            return STORAGE_KEY_PREFIX + flagName;
        }
    }

    @Nullable
    private String storageKeyToFlagName(String configName) {
        if (configName.startsWith(STORAGE_KEY_PREFIX)) {
            return configName.substring(STORAGE_KEY_PREFIX.length());
        } else {
            return null;
        }
    }

    @Override
    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
        ArrayList<String> flagStrings = new ArrayList<>(mCachedFlags.size());
        for (int i = 0; i < mCachedFlags.size(); i++) {
            int key = mCachedFlags.keyAt(i);
            // get the object by the key.
            CachedFlag flag = mCachedFlags.get(key);
            flagStrings.add("  " + RESNAME_PREFIX + flag.name + ": " + flag.value + "\n");
        }
        flagStrings.sort(String.CASE_INSENSITIVE_ORDER);
        pw.println("AreFlagsOverrideable: " + mAreFlagsOverrideable);
        pw.println("Cached FeatureFlags:");
        for (String flagString : flagStrings) {
            pw.print(flagString);
        }
    }

    private static class CachedFlag {
        public final String name;
        public final boolean value;

        private CachedFlag(String name, boolean value) {
            this.name = name;
            this.value = value;
        }
    }

    private static final String STORAGE_KEY_PREFIX = "persist.systemui.flag_";
    private static final String RESNAME_PREFIX = "flag_";
}
+35 −14
Original line number Diff line number Diff line
@@ -17,13 +17,17 @@
package com.android.systemui.flags;

import android.content.Context;
import android.content.res.Resources;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast;

import androidx.annotation.BoolRes;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;

import java.util.ArrayList;
import java.util.HashMap;
@@ -39,13 +43,16 @@ import javax.inject.Inject;
 */
@SysUISingleton
public class FeatureFlags {
    private final FeatureFlagReader mFlagReader;
    private final Resources mResources;
    private final FlagReader mFlagReader;
    private final Context mContext;
    private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
    private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
    private final SparseArray<Boolean> mCachedFlags = new SparseArray<>();

    @Inject
    public FeatureFlags(FeatureFlagReader flagReader, Context context) {
    public FeatureFlags(@Main Resources resources, FlagReader flagReader, Context context) {
        mResources = resources;
        mFlagReader = flagReader;
        mContext = context;

@@ -59,7 +66,7 @@ public class FeatureFlags {
    };

    @VisibleForTesting
    void addFlag(Flag flag) {
    void addFlag(Flag<?> flag) {
        mFlagMap.put(flag.getId(), flag);
    }

@@ -68,7 +75,15 @@ public class FeatureFlags {
     * @return The value of the flag.
     */
    public boolean isEnabled(BooleanFlag flag) {
        return mFlagReader.isEnabled(flag);
        boolean def = flag.getDefault();
        if (flag.hasResourceOverride()) {
            try {
                def = isEnabledInOverlay(flag.getResourceOverride());
            } catch (Resources.NotFoundException e) {
                // no-op
            }
        }
        return mFlagReader.isEnabled(flag.getId(), def);
    }

    /**
@@ -118,13 +133,11 @@ public class FeatureFlags {
    }

    public boolean isPeopleTileEnabled() {
        // TODO(b/202860494): different resource overlays have different values.
        return mFlagReader.isEnabled(R.bool.flag_conversations);
        return isEnabled(Flags.PEOPLE_TILE);
    }

    public boolean isMonetEnabled() {
        // TODO(b/202860494): used in wallpaper picker. Always true, maybe delete.
        return mFlagReader.isEnabled(R.bool.flag_monet);
        return isEnabled(Flags.MONET);
    }

    public boolean isPMLiteEnabled() {
@@ -132,8 +145,7 @@ public class FeatureFlags {
    }

    public boolean isChargingRippleEnabled() {
        // TODO(b/202860494): different resource overlays have different values.
        return mFlagReader.isEnabled(R.bool.flag_charging_ripple);
        return isEnabled(Flags.CHARGING_RIPPLE);
    }

    public boolean isOngoingCallStatusBarChipEnabled() {
@@ -150,8 +162,7 @@ public class FeatureFlags {
    }

    public boolean isSmartspaceEnabled() {
        // TODO(b/202860494): different resource overlays have different values.
        return mFlagReader.isEnabled(R.bool.flag_smartspace);
        return isEnabled(Flags.SMARTSPACE);
    }

    public boolean isSmartspaceDedupingEnabled() {
@@ -163,7 +174,7 @@ public class FeatureFlags {
    }

    public boolean isKeyguardQsUserDetailsShortcutEnabled() {
        return mFlagReader.isEnabled(R.bool.flag_lockscreen_qs_user_detail_shortcut);
        return isEnabled(Flags.QS_USER_DETAIL_SHORTCUT);
    }

    public boolean isSmartSpaceSharedElementTransitionEnabled() {
@@ -199,6 +210,16 @@ public class FeatureFlags {
        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
    }

    private boolean isEnabledInOverlay(@BoolRes int resId) {
        synchronized (mCachedFlags) {
            if (!mCachedFlags.contains(resId)) {
                mCachedFlags.put(resId, mResources.getBoolean(resId));
            }

            return mCachedFlags.get(resId);
        }
    }

    /** Simple interface for beinga alerted when a specific flag changes value. */
    public interface Listener {
        /** */
Loading