Merge "Add flag listeners." into sc-v2-dev

This commit is contained in:
Dave Mankoff
2021-08-06 16:52:59 +00:00
committed by Android (Google) Code Review
4 changed files with 164 additions and 22 deletions

View File

@@ -56,4 +56,16 @@ public interface FlagReaderPlugin extends Plugin {
default double getValue(int id, double def) { default double getValue(int id, double def) {
return def; return def;
} }
/** Add a listener to be alerted when any flag changes. */
default void addListener(Listener listener) {}
/** Remove a listener to be alerted when any flag changes. */
default void removeListener(Listener listener) {}
/** A simple listener to be alerted when a flag changes. */
interface Listener {
/** */
void onFlagChanged(int id);
}
} }

View File

@@ -114,6 +114,14 @@ public class FeatureFlagReader {
return mPlugin.getValue(flag.getId(), flag.getDefault()); return mPlugin.getValue(flag.getId(), flag.getDefault());
} }
void addListener(FlagReaderPlugin.Listener listener) {
mPlugin.addListener(listener);
}
void removeListener(FlagReaderPlugin.Listener listener) {
mPlugin.removeListener(listener);
}
/** /**
* Returns true if the specified feature flag has been enabled. * Returns true if the specified feature flag has been enabled.
* *

View File

@@ -19,11 +19,14 @@ package com.android.systemui.flags;
import android.content.Context; import android.content.Context;
import android.util.FeatureFlagUtils; import android.util.FeatureFlagUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.FlagReaderPlugin;
import java.lang.reflect.Field; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
@@ -37,11 +40,26 @@ import javax.inject.Inject;
public class FeatureFlags { public class FeatureFlags {
private final FeatureFlagReader mFlagReader; private final FeatureFlagReader mFlagReader;
private final Context mContext; private final Context mContext;
private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
@Inject @Inject
public FeatureFlags(FeatureFlagReader flagReader, Context context) { public FeatureFlags(FeatureFlagReader flagReader, Context context) {
mFlagReader = flagReader; mFlagReader = flagReader;
mContext = context; mContext = context;
flagReader.addListener(mListener);
}
private final FlagReaderPlugin.Listener mListener = id -> {
if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
}
};
@VisibleForTesting
void addFlag(Flag flag) {
mFlagMap.put(flag.getId(), flag);
} }
/** /**
@@ -92,6 +110,20 @@ public class FeatureFlags {
return mFlagReader.getValue(flag); return mFlagReader.getValue(flag);
} }
/** Add a listener for a specific flag. */
public void addFlagListener(Flag<?> flag, Listener listener) {
mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
mListeners.get(flag.getId()).add(listener);
mFlagMap.putIfAbsent(flag.getId(), flag);
}
/** Remove a listener for a specific flag. */
public void removeFlagListener(Flag<?> flag, Listener listener) {
if (mListeners.containsKey(flag.getId())) {
mListeners.get(flag.getId()).remove(listener);
}
}
public boolean isNewNotifPipelineEnabled() { public boolean isNewNotifPipelineEnabled() {
return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2); return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2);
} }
@@ -160,27 +192,6 @@ public class FeatureFlags {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL); 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. */ /** Simple interface for beinga alerted when a specific flag changes value. */
public interface Listener { public interface Listener {
/** */ /** */

View File

@@ -0,0 +1,111 @@
/*
* 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.assertThat;
import static org.mockito.Mockito.verify;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FlagReaderPlugin;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
public class FeatureFlagsTest extends SysuiTestCase {
@Mock FeatureFlagReader mFeatureFlagReader;
private FeatureFlags mFeatureFlags;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mFeatureFlags = new FeatureFlags(mFeatureFlagReader, getContext());
}
@Test
public void testAddListener() {
Flag<?> flag = new BooleanFlag(1);
mFeatureFlags.addFlag(flag);
// Assert and capture that a plugin listener was added.
ArgumentCaptor<FlagReaderPlugin.Listener> pluginListenerCaptor =
ArgumentCaptor.forClass(FlagReaderPlugin.Listener.class);
verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
FlagReaderPlugin.Listener pluginListener = pluginListenerCaptor.getValue();
// Signal a change. No listeners, so no real effect.
pluginListener.onFlagChanged(flag.getId());
// Add a listener for the flag
final Flag<?>[] changedFlag = {null};
FeatureFlags.Listener listener = f -> changedFlag[0] = f;
mFeatureFlags.addFlagListener(flag, listener);
// No changes seen yet.
assertThat(changedFlag[0]).isNull();
// Signal a change.
pluginListener.onFlagChanged(flag.getId());
// Assert that the change was for the correct flag.
assertThat(changedFlag[0]).isEqualTo(flag);
}
@Test
public void testRemoveListener() {
Flag<?> flag = new BooleanFlag(1);
mFeatureFlags.addFlag(flag);
// Assert and capture that a plugin listener was added.
ArgumentCaptor<FlagReaderPlugin.Listener> pluginListenerCaptor =
ArgumentCaptor.forClass(FlagReaderPlugin.Listener.class);
verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
FlagReaderPlugin.Listener pluginListener = pluginListenerCaptor.getValue();
// Add a listener for the flag
final Flag<?>[] changedFlag = {null};
FeatureFlags.Listener listener = f -> changedFlag[0] = f;
mFeatureFlags.addFlagListener(flag, listener);
// Signal a change.
pluginListener.onFlagChanged(flag.getId());
// Assert that the change was for the correct flag.
assertThat(changedFlag[0]).isEqualTo(flag);
changedFlag[0] = null;
// Now remove the listener.
mFeatureFlags.removeFlagListener(flag, listener);
// Signal a change.
pluginListener.onFlagChanged(flag.getId());
// Assert that the change was not triggered
assertThat(changedFlag[0]).isNull();
}
}