From 2d1ab9a193f2a4b681d0ab20d0dbfbaf644df00e Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 3 Oct 2017 11:16:44 -0700 Subject: [PATCH] Display a list of feature flags Bug: 36222960 Test: robotests Change-Id: I31fbe7f4d42e72846aa4f025ebcf8ea8a1b6d2fd --- .../featureflags/FeatureFlagPreference.java | 45 +++++++++++ .../featureflags/FeatureFlagsDashboard.java | 5 +- .../FeatureFlagsPreferenceController.java | 81 +++++++++++++++++++ .../src/android/util/FeatureFlagUtils.java | 18 +++++ .../FeatureFlagPreferenceControllerTest.java | 72 +++++++++++++++++ .../FeatureFlagPreferenceTest.java | 61 ++++++++++++++ 6 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/development/featureflags/FeatureFlagPreference.java create mode 100644 src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java new file mode 100644 index 00000000000..80851d35dc3 --- /dev/null +++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 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.settings.development.featureflags; + +import android.content.Context; +import android.support.v14.preference.SwitchPreference; +import android.util.FeatureFlagUtils; + +public class FeatureFlagPreference extends SwitchPreference { + + private final String mKey; + + public FeatureFlagPreference(Context context, String key) { + super(context); + mKey = key; + setKey(key); + setTitle(key); + setCheckedInternal(FeatureFlagUtils.isEnabled(mKey)); + } + + @Override + public void setChecked(boolean isChecked) { + setCheckedInternal(isChecked); + FeatureFlagUtils.setEnabled(mKey, isChecked); + } + + private void setCheckedInternal(boolean isChecked) { + super.setChecked(isChecked); + setSummary(Boolean.toString(isChecked)); + } +} diff --git a/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java b/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java index ee2258d25fe..998e4312c85 100644 --- a/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java +++ b/src/com/android/settings/development/featureflags/FeatureFlagsDashboard.java @@ -23,6 +23,7 @@ import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settingslib.core.AbstractPreferenceController; +import java.util.ArrayList; import java.util.List; public class FeatureFlagsDashboard extends DashboardFragment { @@ -51,6 +52,8 @@ public class FeatureFlagsDashboard extends DashboardFragment { @Override protected List getPreferenceControllers(Context context) { - return null; + final List controllers = new ArrayList<>(); + controllers.add(new FeatureFlagsPreferenceController(context, getLifecycle())); + return controllers; } } diff --git a/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java b/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java new file mode 100644 index 00000000000..7c00591c02f --- /dev/null +++ b/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 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.settings.development.featureflags; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.util.FeatureFlagUtils; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; + +import java.util.Map; + +public class FeatureFlagsPreferenceController extends AbstractPreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart { + + private PreferenceScreen mScreen; + + public FeatureFlagsPreferenceController(Context context, Lifecycle lifecycle) { + super(context); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return null; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mScreen = screen; + } + + @Override + public void onStart() { + if (mScreen == null) { + return; + } + final Map featureMap = FeatureFlagUtils.getAllFeatureFlags(); + if (featureMap == null) { + return; + } + mScreen.removeAll(); + final Context prefContext = mScreen.getContext(); + for (String prefixedFeature : featureMap.keySet()) { + if (prefixedFeature.startsWith(FeatureFlagUtils.FFLAG_PREFIX) + && !prefixedFeature.startsWith(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX)) { + final String feature = prefixedFeature.substring( + FeatureFlagUtils.FFLAG_PREFIX.length()); + final Preference pref = new FeatureFlagPreference(prefContext, feature); + mScreen.addPreference(pref); + } + } + } +} diff --git a/tests/robotests/src/android/util/FeatureFlagUtils.java b/tests/robotests/src/android/util/FeatureFlagUtils.java index 6bc0557a2b7..500884a6c3b 100644 --- a/tests/robotests/src/android/util/FeatureFlagUtils.java +++ b/tests/robotests/src/android/util/FeatureFlagUtils.java @@ -19,6 +19,9 @@ package android.util; import android.os.SystemProperties; import android.text.TextUtils; +import java.util.HashMap; +import java.util.Map; + /** * This class is only needed to get around Robolectric issue. */ @@ -43,4 +46,19 @@ public class FeatureFlagUtils { value = SystemProperties.get(FFLAG_PREFIX + feature); return Boolean.parseBoolean(value); } + + /** + * Override feature flag to new state. + */ + public static void setEnabled(String feature, boolean enabled) { + SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false"); + } + + + public static Map getAllFeatureFlags() { + final Map features = new HashMap<>(); + features.put(FFLAG_PREFIX + "abc", "false"); + features.put(FFLAG_OVERRIDE_PREFIX + "abc", "true"); + return features; + } } diff --git a/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java new file mode 100644 index 00000000000..3d95cf44ee1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceControllerTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 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.settings.development.featureflags; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class FeatureFlagPreferenceControllerTest { + + @Mock + private PreferenceScreen mScreen; + private Context mContext; + private Lifecycle mLifecycle; + private FeatureFlagsPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mLifecycle = new Lifecycle(); + mController = new FeatureFlagsPreferenceController(mContext, mLifecycle); + when(mScreen.getContext()).thenReturn(mContext); + mController.displayPreference(mScreen); + } + + @Test + public void verifyConstants() { + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.getPreferenceKey()).isNull(); + } + + @Test + public void onStart_shouldRefreshFeatureFlags() { + mLifecycle.onStart(); + + verify(mScreen).removeAll(); + verify(mScreen).addPreference(any(FeatureFlagPreference.class)); + } +} diff --git a/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java new file mode 100644 index 00000000000..11099b16c21 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPreferenceTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 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.settings.development.featureflags; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class FeatureFlagPreferenceTest { + + private static final String KEY = "feature_key"; + + private Context mContext; + private FeatureFlagPreference mPreference; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mPreference = new FeatureFlagPreference(mContext, KEY); + } + + @Test + public void constructor_shouldSetTitleAndSummary() { + assertThat(mPreference.getTitle()).isEqualTo(KEY); + assertThat(mPreference.getSummary()).isEqualTo("false"); + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void toggle_shouldUpdateSummary() { + mPreference.setChecked(true); + + assertThat(mPreference.getSummary()).isEqualTo("true"); + assertThat(mPreference.isChecked()).isTrue(); + } +}