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
This commit is contained in:
Jacky Wang
2024-10-13 14:28:16 +00:00
committed by Android (Google) Code Review
5 changed files with 82 additions and 39 deletions

View File

@@ -53,6 +53,7 @@ import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.preference.PreferenceScreenCreator;
import com.android.settingslib.search.Indexable; import com.android.settingslib.search.Indexable;
import java.util.ArrayList; import java.util.ArrayList;
@@ -60,6 +61,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -98,7 +100,8 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
mDashboardFeatureProvider = mDashboardFeatureProvider =
FeatureFactory.getFeatureFactory().getDashboardFeatureProvider(); FeatureFactory.getFeatureFactory().getDashboardFeatureProvider();
if (!isCatalystEnabled()) { PreferenceScreenCreator preferenceScreenCreator = getPreferenceScreenCreator();
if (preferenceScreenCreator == null || !preferenceScreenCreator.hasCompleteHierarchy()) {
// Load preference controllers from code // Load preference controllers from code
final List<AbstractPreferenceController> controllersFromCode = final List<AbstractPreferenceController> controllersFromCode =
createPreferenceControllers(context); createPreferenceControllers(context);
@@ -383,8 +386,12 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
return; return;
} }
PreferenceScreen screen; PreferenceScreen screen;
if (isCatalystEnabled()) { PreferenceScreenCreator preferenceScreenCreator = getPreferenceScreenCreator();
if (preferenceScreenCreator != null) {
screen = createPreferenceScreen(); screen = createPreferenceScreen();
if (!preferenceScreenCreator.hasCompleteHierarchy()) {
removeControllersForHybridMode();
}
setPreferenceScreen(screen); setPreferenceScreen(screen);
requireActivity().setTitle(screen.getTitle()); requireActivity().setTitle(screen.getTitle());
} else { } else {
@@ -395,13 +402,42 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
displayResourceTilesToScreen(screen); 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. */ /** Returns if catalyst is enabled on current screen. */
protected final boolean isCatalystEnabled() { protected final boolean isCatalystEnabled() {
return getPreferenceScreenCreator() != null;
}
private @Nullable PreferenceScreenCreator getPreferenceScreenCreator() {
if (!Flags.catalyst()) { if (!Flags.catalyst()) {
return false; return null;
} }
Context context = getContext(); Context context = getContext();
return context != null ? getPreferenceScreenCreator(context) != null : false; return context != null ? getPreferenceScreenCreator(context) : null;
} }
/** /**

View File

@@ -34,6 +34,8 @@ class DisplayScreen : PreferenceScreenCreator, PreferenceAvailabilityProvider {
override fun isFlagEnabled(context: Context) = Flags.catalystDisplaySettingsScreen() override fun isFlagEnabled(context: Context) = Flags.catalystDisplaySettingsScreen()
override fun hasCompleteHierarchy() = false
override fun fragmentClass() = DisplaySettings::class.java override fun fragmentClass() = DisplaySettings::class.java
override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}

View File

@@ -36,10 +36,11 @@ class SoundScreen : PreferenceScreenCreator {
override fun isFlagEnabled(context: Context): Boolean = Flags.catalystSoundScreen() override fun isFlagEnabled(context: Context): Boolean = Flags.catalystSoundScreen()
override fun hasCompleteHierarchy() = false
override fun fragmentClass(): Class<out Fragment>? = SoundSettings::class.java override fun fragmentClass(): Class<out Fragment>? = SoundSettings::class.java
override fun getPreferenceHierarchy(context: Context) = override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {}
preferenceHierarchy(this) {}
companion object { companion object {
const val KEY = "sound_screen" const val KEY = "sound_screen"

View File

@@ -17,24 +17,32 @@ package com.android.settings.display
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.res.Resources import android.content.res.Resources
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 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 com.google.common.truth.Truth.assertThat
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.stub import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class DisplayScreenTest { class DisplayScreenTest : CatalystScreenTestCase() {
val preferenceScreenCreator = DisplayScreen()
override val preferenceScreenCreator = DisplayScreen()
override val flagName: String
get() = Flags.FLAG_CATALYST_DISPLAY_SETTINGS_SCREEN
private val mockResources = mock<Resources>() private val mockResources = mock<Resources>()
private val context = private val contextWrapper =
object : ContextWrapper(ApplicationProvider.getApplicationContext()) { object : ContextWrapper(context) {
override fun getResources(): Resources = mockResources override fun getResources(): Resources = mockResources
} }
@@ -47,13 +55,25 @@ class DisplayScreenTest {
fun isAvailable_configTrue_shouldReturnTrue() { fun isAvailable_configTrue_shouldReturnTrue() {
mockResources.stub { on { getBoolean(anyInt()) } doReturn true } mockResources.stub { on { getBoolean(anyInt()) } doReturn true }
assertThat(preferenceScreenCreator.isAvailable(context)).isTrue() assertThat(preferenceScreenCreator.isAvailable(contextWrapper)).isTrue()
} }
@Test @Test
fun isAvailable_configFalse_shouldReturnFalse() { fun isAvailable_configFalse_shouldReturnFalse() {
mockResources.stub { on { getBoolean(anyInt()) } doReturn false } 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()
} }
} }

View File

@@ -15,39 +15,23 @@
*/ */
package com.android.settings.notification 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 androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.flags.Flags import com.android.settings.flags.Flags
import com.android.settingslib.preference.CatalystScreenTestCase
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class SoundScreenTest { class SoundScreenTest : CatalystScreenTestCase() {
@get:Rule val setFlagsRule = SetFlagsRule()
private val context: Context = ApplicationProvider.getApplicationContext() override val preferenceScreenCreator = SoundScreen()
private val soundScreen = SoundScreen()
override val flagName: String
get() = Flags.FLAG_CATALYST_SOUND_SCREEN
@Test @Test
fun key() { 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()
}
} }