Merge "Add new Flag class. New FlagReaderPlugin." into sc-v2-dev am: d80f48c3bd
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15405311 Change-Id: I3a0eb89ddbc436efc1ef837551f2ab5f8401c8db
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
interface Flag<T> {
|
||||
val id: Int
|
||||
val default: T
|
||||
}
|
||||
|
||||
data class BooleanFlag @JvmOverloads constructor(
|
||||
override val id: Int,
|
||||
override val default: Boolean = false
|
||||
) : Flag<Boolean>
|
||||
|
||||
data class StringFlag @JvmOverloads constructor(
|
||||
override val id: Int,
|
||||
override val default: String = ""
|
||||
) : Flag<String>
|
||||
|
||||
data class IntFlag @JvmOverloads constructor(
|
||||
override val id: Int,
|
||||
override val default: Int = 0
|
||||
) : Flag<Int>
|
||||
|
||||
data class LongFlag @JvmOverloads constructor(
|
||||
override val id: Int,
|
||||
override val default: Long = 0
|
||||
) : Flag<Long>
|
||||
|
||||
data class FloatFlag @JvmOverloads constructor(
|
||||
override val id: Int,
|
||||
override val default: Float = 0f
|
||||
) : Flag<Float>
|
||||
|
||||
data class DoubleFlag @JvmOverloads constructor(
|
||||
override val id: Int,
|
||||
override val default: Double = 0.0
|
||||
) : Flag<Double>
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* List of {@link Flag} objects for use in SystemUI.
|
||||
*/
|
||||
public class Flags {
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.plugins;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Plugin for loading flag values from an alternate source of truth.
|
||||
*/
|
||||
@ProvidesInterface(action = FlagReaderPlugin.ACTION, version = FlagReaderPlugin.VERSION)
|
||||
public interface FlagReaderPlugin extends Plugin {
|
||||
int VERSION = 1;
|
||||
String ACTION = "com.android.systemui.flags.FLAG_READER_PLUGIN";
|
||||
|
||||
/** Returns a boolean value for the given flag. */
|
||||
default boolean isEnabled(int id, boolean def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
/** Returns a string value for the given flag id. */
|
||||
default String getValue(int id, String def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
/** Returns a int value for the given flag. */
|
||||
default int getValue(int id, int def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
/** Returns a long value for the given flag. */
|
||||
default long getValue(int id, long def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
/** Returns a float value for the given flag. */
|
||||
default float getValue(int id, float def) {
|
||||
return def;
|
||||
}
|
||||
|
||||
/** Returns a double value for the given flag. */
|
||||
default double getValue(int id, double def) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.flags;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -25,6 +26,9 @@ import androidx.annotation.Nullable;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.plugins.FlagReaderPlugin;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.util.wrapper.BuildInfo;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -54,18 +58,60 @@ import javax.inject.Inject;
|
||||
public class FeatureFlagReader {
|
||||
private final Resources mResources;
|
||||
private final boolean mAreFlagsOverrideable;
|
||||
private final PluginManager mPluginManager;
|
||||
private final SystemPropertiesHelper mSystemPropertiesHelper;
|
||||
private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();
|
||||
|
||||
private FlagReaderPlugin mPlugin = new FlagReaderPlugin(){};
|
||||
|
||||
@Inject
|
||||
public FeatureFlagReader(
|
||||
@Main Resources resources,
|
||||
BuildInfo build,
|
||||
PluginManager pluginManager,
|
||||
SystemPropertiesHelper systemPropertiesHelper) {
|
||||
mResources = resources;
|
||||
mPluginManager = pluginManager;
|
||||
mSystemPropertiesHelper = systemPropertiesHelper;
|
||||
mAreFlagsOverrideable =
|
||||
build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
|
||||
|
||||
mPluginManager.addPluginListener(mPluginListener, FlagReaderPlugin.class);
|
||||
}
|
||||
|
||||
private final PluginListener<FlagReaderPlugin> mPluginListener =
|
||||
new PluginListener<FlagReaderPlugin>() {
|
||||
public void onPluginConnected(FlagReaderPlugin plugin, Context context) {
|
||||
mPlugin = plugin;
|
||||
}
|
||||
|
||||
public void onPluginDisconnected(FlagReaderPlugin plugin) {
|
||||
mPlugin = new FlagReaderPlugin() {};
|
||||
}
|
||||
};
|
||||
|
||||
boolean isEnabled(BooleanFlag flag) {
|
||||
return mPlugin.isEnabled(flag.getId(), flag.getDefault());
|
||||
}
|
||||
|
||||
String getValue(StringFlag flag) {
|
||||
return mPlugin.getValue(flag.getId(), flag.getDefault());
|
||||
}
|
||||
|
||||
int getValue(IntFlag flag) {
|
||||
return mPlugin.getValue(flag.getId(), flag.getDefault());
|
||||
}
|
||||
|
||||
long getValue(LongFlag flag) {
|
||||
return mPlugin.getValue(flag.getId(), flag.getDefault());
|
||||
}
|
||||
|
||||
float getValue(FloatFlag flag) {
|
||||
return mPlugin.getValue(flag.getId(), flag.getDefault());
|
||||
}
|
||||
|
||||
double getValue(DoubleFlag flag) {
|
||||
return mPlugin.getValue(flag.getId(), flag.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,10 @@ import android.util.FeatureFlagUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -40,6 +44,54 @@ public class FeatureFlags {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag The {@link BooleanFlag} of interest.
|
||||
* @return The value of the flag.
|
||||
*/
|
||||
public boolean isEnabled(BooleanFlag flag) {
|
||||
return mFlagReader.isEnabled(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag The {@link StringFlag} of interest.
|
||||
* @return The value of the flag.
|
||||
*/
|
||||
public String getValue(StringFlag flag) {
|
||||
return mFlagReader.getValue(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag The {@link IntFlag} of interest.
|
||||
* @return The value of the flag.
|
||||
*/
|
||||
public int getValue(IntFlag flag) {
|
||||
return mFlagReader.getValue(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag The {@link LongFlag} of interest.
|
||||
* @return The value of the flag.
|
||||
*/
|
||||
public long getValue(LongFlag flag) {
|
||||
return mFlagReader.getValue(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag The {@link FloatFlag} of interest.
|
||||
* @return The value of the flag.
|
||||
*/
|
||||
public float getValue(FloatFlag flag) {
|
||||
return mFlagReader.getValue(flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag The {@link DoubleFlag} of interest.
|
||||
* @return The value of the flag.
|
||||
*/
|
||||
public double getValue(DoubleFlag flag) {
|
||||
return mFlagReader.getValue(flag);
|
||||
}
|
||||
|
||||
public boolean isNewNotifPipelineEnabled() {
|
||||
return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2);
|
||||
}
|
||||
@@ -107,4 +159,31 @@ public class FeatureFlags {
|
||||
public static boolean isProviderModelSettingEnabled(Context context) {
|
||||
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
|
||||
}
|
||||
|
||||
private Map<Integer, Flag<?>> collectFlags() {
|
||||
Map<Integer, Flag<?>> flags = new HashMap<>();
|
||||
|
||||
Field[] fields = this.getClass().getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
Class<?> t = field.getType();
|
||||
if (Flag.class.isAssignableFrom(t)) {
|
||||
try {
|
||||
//flags.add((Flag<?>) field.get(null));
|
||||
Flag flag = (Flag) field.get(null);
|
||||
flags.put(flag.getId(), flag);
|
||||
} catch (IllegalAccessException e) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
/** Simple interface for beinga alerted when a specific flag changes value. */
|
||||
public interface Listener {
|
||||
/** */
|
||||
void onFlagChanged(Flag<?> flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.util.wrapper.BuildInfo;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -43,6 +44,7 @@ import org.mockito.MockitoAnnotations;
|
||||
public class FeatureFlagReaderTest extends SysuiTestCase {
|
||||
@Mock private Resources mResources;
|
||||
@Mock private BuildInfo mBuildInfo;
|
||||
@Mock private PluginManager mPluginManager;
|
||||
@Mock private SystemPropertiesHelper mSystemPropertiesHelper;
|
||||
|
||||
private FeatureFlagReader mReader;
|
||||
@@ -63,7 +65,8 @@ public class FeatureFlagReaderTest extends SysuiTestCase {
|
||||
private void initialize(boolean isDebuggable, boolean isOverrideable) {
|
||||
when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable);
|
||||
when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable);
|
||||
mReader = new FeatureFlagReader(mResources, mBuildInfo, mSystemPropertiesHelper);
|
||||
mReader = new FeatureFlagReader(
|
||||
mResources, mBuildInfo, mPluginManager, mSystemPropertiesHelper);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SmallTest
|
||||
public class FlagsTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testDuplicateFlagIdCheckWorks() {
|
||||
List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
|
||||
Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
|
||||
|
||||
assertWithMessage(generateAssertionMessage(duplicates))
|
||||
.that(duplicates.size()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoDuplicateFlagIds() {
|
||||
List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
|
||||
Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
|
||||
|
||||
assertWithMessage(generateAssertionMessage(duplicates))
|
||||
.that(duplicates.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("Duplicate flag keys found: {");
|
||||
for (int id : duplicates.keySet()) {
|
||||
stringBuilder
|
||||
.append(" ")
|
||||
.append(id)
|
||||
.append(": [")
|
||||
.append(String.join(", ", duplicates.get(id)))
|
||||
.append("]");
|
||||
}
|
||||
stringBuilder.append(" }");
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
|
||||
List<Pair<String, Flag<?>>> flags = new ArrayList<>();
|
||||
|
||||
Field[] fields = clz.getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
Class<?> t = field.getType();
|
||||
if (Flag.class.isAssignableFrom(t)) {
|
||||
try {
|
||||
flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
|
||||
} catch (IllegalAccessException e) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
|
||||
Map<Integer, List<String>> grouping = new HashMap<>();
|
||||
|
||||
for (Pair<String, Flag<?>> flag : flags) {
|
||||
grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
|
||||
grouping.get(flag.second.getId()).add(flag.first);
|
||||
}
|
||||
|
||||
Map<Integer, List<String>> result = new HashMap<>();
|
||||
for (Integer id : grouping.keySet()) {
|
||||
if (grouping.get(id).size() > 1) {
|
||||
result.put(id, grouping.get(id));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class DuplicateFlagContainer {
|
||||
public static final BooleanFlag A_FLAG = new BooleanFlag(0);
|
||||
public static final BooleanFlag B_FLAG = new BooleanFlag(0);
|
||||
public static final StringFlag C_FLAG = new StringFlag(0);
|
||||
|
||||
public static final BooleanFlag D_FLAG = new BooleanFlag(1);
|
||||
|
||||
public static final DoubleFlag E_FLAG = new DoubleFlag(3);
|
||||
public static final DoubleFlag F_FLAG = new DoubleFlag(3);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user