Merge changes from topic "catalyst" into main
* changes:
[Catalyst] Use hybrid mode for sound screen
[Catalyst] Use hybrid mode for display screen
[Catalyst] Support hybrid mode
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 4d53772..92e99cf 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -53,6 +53,7 @@
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.preference.PreferenceScreenCreator;
import com.android.settingslib.search.Indexable;
import java.util.ArrayList;
@@ -60,6 +61,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -98,7 +100,8 @@
mDashboardFeatureProvider =
FeatureFactory.getFeatureFactory().getDashboardFeatureProvider();
- if (!isCatalystEnabled()) {
+ PreferenceScreenCreator preferenceScreenCreator = getPreferenceScreenCreator();
+ if (preferenceScreenCreator == null || !preferenceScreenCreator.hasCompleteHierarchy()) {
// Load preference controllers from code
final List<AbstractPreferenceController> controllersFromCode =
createPreferenceControllers(context);
@@ -383,8 +386,12 @@
return;
}
PreferenceScreen screen;
- if (isCatalystEnabled()) {
+ PreferenceScreenCreator preferenceScreenCreator = getPreferenceScreenCreator();
+ if (preferenceScreenCreator != null) {
screen = createPreferenceScreen();
+ if (!preferenceScreenCreator.hasCompleteHierarchy()) {
+ removeControllersForHybridMode();
+ }
setPreferenceScreen(screen);
requireActivity().setTitle(screen.getTitle());
} else {
@@ -395,13 +402,42 @@
displayResourceTilesToScreen(screen);
}
+ /**
+ * Removes preference controllers that have been migrated to catalyst.
+ *
+ * In hybrid mode, preference screen is inflated from XML resource, while preference metadata
+ * in the preference hierarchy are used to update preference widget UI. To avoid conflict,
+ * remove the preference controllers.
+ */
+ private void removeControllersForHybridMode() {
+ Set<String> keys = getPreferenceKeysInHierarchy();
+ Iterator<AbstractPreferenceController> iterator = mControllers.iterator();
+ while (iterator.hasNext()) {
+ AbstractPreferenceController controller = iterator.next();
+ String key = controller.getPreferenceKey();
+ if (keys.contains(key)) {
+ Log.i(TAG, "Remove preference controller for " + key);
+ iterator.remove();
+ List<AbstractPreferenceController> controllers = mPreferenceControllers.get(
+ controller.getClass());
+ if (controllers != null) {
+ controllers.remove(controller);
+ }
+ }
+ }
+ }
+
/** Returns if catalyst is enabled on current screen. */
protected final boolean isCatalystEnabled() {
+ return getPreferenceScreenCreator() != null;
+ }
+
+ private @Nullable PreferenceScreenCreator getPreferenceScreenCreator() {
if (!Flags.catalyst()) {
- return false;
+ return null;
}
Context context = getContext();
- return context != null ? getPreferenceScreenCreator(context) != null : false;
+ return context != null ? getPreferenceScreenCreator(context) : null;
}
/**
diff --git a/src/com/android/settings/display/DisplayScreen.kt b/src/com/android/settings/display/DisplayScreen.kt
index fceb18a..9886e4a 100644
--- a/src/com/android/settings/display/DisplayScreen.kt
+++ b/src/com/android/settings/display/DisplayScreen.kt
@@ -34,6 +34,8 @@
override fun isFlagEnabled(context: Context) = Flags.catalystDisplaySettingsScreen()
+ override fun hasCompleteHierarchy() = false
+
override fun fragmentClass() = DisplaySettings::class.java
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
diff --git a/src/com/android/settings/notification/SoundScreen.kt b/src/com/android/settings/notification/SoundScreen.kt
index ecb0c85..f1f2749 100644
--- a/src/com/android/settings/notification/SoundScreen.kt
+++ b/src/com/android/settings/notification/SoundScreen.kt
@@ -36,12 +36,13 @@
override fun isFlagEnabled(context: Context): Boolean = Flags.catalystSoundScreen()
+ override fun hasCompleteHierarchy() = false
+
override fun fragmentClass(): Class<out Fragment>? = SoundSettings::class.java
- override fun getPreferenceHierarchy(context: Context) =
- preferenceHierarchy(this) {}
+ override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
companion object {
const val KEY = "sound_screen"
}
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt b/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt
index 50c706f..d869b84 100644
--- a/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt
+++ b/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt
@@ -17,24 +17,32 @@
import android.content.ContextWrapper
import android.content.res.Resources
-import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.widget.LockPatternUtils
+import com.android.settings.flags.Flags
+import com.android.settings.testutils.FakeFeatureFactory
+import com.android.settingslib.preference.CatalystScreenTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
-class DisplayScreenTest {
- val preferenceScreenCreator = DisplayScreen()
+class DisplayScreenTest : CatalystScreenTestCase() {
+
+ override val preferenceScreenCreator = DisplayScreen()
+
+ override val flagName: String
+ get() = Flags.FLAG_CATALYST_DISPLAY_SETTINGS_SCREEN
private val mockResources = mock<Resources>()
- private val context =
- object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
+ private val contextWrapper =
+ object : ContextWrapper(context) {
override fun getResources(): Resources = mockResources
}
@@ -47,13 +55,25 @@
fun isAvailable_configTrue_shouldReturnTrue() {
mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
- assertThat(preferenceScreenCreator.isAvailable(context)).isTrue()
+ assertThat(preferenceScreenCreator.isAvailable(contextWrapper)).isTrue()
}
@Test
fun isAvailable_configFalse_shouldReturnFalse() {
mockResources.stub { on { getBoolean(anyInt()) } doReturn false }
- assertThat(preferenceScreenCreator.isAvailable(context)).isFalse()
+ assertThat(preferenceScreenCreator.isAvailable(contextWrapper)).isFalse()
+ }
+
+ override fun migration() {
+ // avoid UnsupportedOperationException when getDisplay from context
+ System.setProperty("robolectric.createActivityContexts", "true")
+
+ val lockPatternUtils = mock<LockPatternUtils> { on { isSecure(anyInt()) } doReturn true }
+ FakeFeatureFactory.setupForTest().securityFeatureProvider.stub {
+ on { getLockPatternUtils(any()) } doReturn lockPatternUtils
+ }
+
+ super.migration()
}
}
diff --git a/tests/robotests/src/com/android/settings/notification/SoundScreenTest.kt b/tests/robotests/src/com/android/settings/notification/SoundScreenTest.kt
index 83b29d2..1333ed5 100644
--- a/tests/robotests/src/com/android/settings/notification/SoundScreenTest.kt
+++ b/tests/robotests/src/com/android/settings/notification/SoundScreenTest.kt
@@ -15,39 +15,23 @@
*/
package com.android.settings.notification
-import android.content.Context
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
-import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.flags.Flags
+import com.android.settingslib.preference.CatalystScreenTestCase
import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
-class SoundScreenTest {
- @get:Rule val setFlagsRule = SetFlagsRule()
- private val context: Context = ApplicationProvider.getApplicationContext()
- private val soundScreen = SoundScreen()
+class SoundScreenTest : CatalystScreenTestCase() {
+
+ override val preferenceScreenCreator = SoundScreen()
+
+ override val flagName: String
+ get() = Flags.FLAG_CATALYST_SOUND_SCREEN
@Test
fun key() {
- assertThat(soundScreen.key).isEqualTo(SoundScreen.KEY)
+ assertThat(preferenceScreenCreator.key).isEqualTo(SoundScreen.KEY)
}
-
- @Test
- @EnableFlags(Flags.FLAG_CATALYST_SOUND_SCREEN)
- fun isFlagEnabled_returnTrue() {
- assertThat(soundScreen.isFlagEnabled(context)).isTrue()
- }
-
- @Test
- @DisableFlags(Flags.FLAG_CATALYST_SOUND_SCREEN)
- fun isFlagEnabled_returnFalse() {
- assertThat(soundScreen.isFlagEnabled(context)).isFalse()
- }
-
-}
\ No newline at end of file
+}