summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jacky Wang <jiannan@google.com> 2025-01-21 15:41:20 +0800
committer Jacky Wang <jiannan@google.com> 2025-01-23 21:20:03 +0800
commit5a87bd9aa9aae67e376acd27a2b30dd89a7473ea (patch)
tree46c4d2e41a1595329d60f0f775d8df4841736ac8
parent76666b308c7f889293ec2f09d6e5bcb53d7dc6e0 (diff)
[Catalyst] Support parameterized screens
Bug: 388420844 Flag: com.android.settings.flags.catalyst Test: devtool Change-Id: Iceca5d5b58187708156bbda8927e1622679d12ba
-rw-r--r--packages/SettingsLib/Graph/graph.proto8
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt39
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt7
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt89
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt18
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt7
-rw-r--r--packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt130
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt15
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt31
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt7
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt63
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt28
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt5
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt11
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt7
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt13
16 files changed, 375 insertions, 103 deletions
diff --git a/packages/SettingsLib/Graph/graph.proto b/packages/SettingsLib/Graph/graph.proto
index 33a7df4c6ba8..a834947144a0 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -26,6 +26,14 @@ message PreferenceScreenProto {
optional PreferenceGroupProto root = 2;
// If the preference screen provides complete hierarchy by source code.
optional bool complete_hierarchy = 3;
+ // Parameterized screens (not recursive, provided on the top level only)
+ repeated ParameterizedPreferenceScreenProto parameterized_screens = 4;
+}
+
+// Proto of parameterized preference screen
+message ParameterizedPreferenceScreenProto {
+ optional BundleProto args = 1;
+ optional PreferenceScreenProto screen = 2;
}
// Proto of PreferenceGroup.
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index adffd206d552..27ce1c7246e6 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -18,12 +18,14 @@ package com.android.settingslib.graph
import android.app.Application
import android.os.Bundle
+import android.os.Parcelable
import android.os.SystemClock
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.ipc.ApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale
@@ -59,10 +61,9 @@ class GetPreferenceGraphApiHandler(
var success = false
try {
val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
- if (request.screenKeys.isEmpty()) {
- PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync {
- builder.addPreferenceScreenFromRegistry(it)
- }
+ if (request.screens.isEmpty()) {
+ val factories = PreferenceScreenRegistry.preferenceScreenMetadataFactories
+ factories.forEachAsync { _, factory -> builder.addPreferenceScreen(factory) }
for (provider in preferenceScreenProviders) {
builder.addPreferenceScreenProvider(provider)
}
@@ -84,15 +85,15 @@ class GetPreferenceGraphApiHandler(
/**
* Request of [GetPreferenceGraphApiHandler].
*
- * @param screenKeys screen keys of the preference graph
- * @param visitedScreens keys of the visited preference screen
+ * @param screens screens of the preference graph
+ * @param visitedScreens visited preference screens
* @param locale locale of the preference graph
*/
data class GetPreferenceGraphRequest
@JvmOverloads
constructor(
- val screenKeys: Set<String> = setOf(),
- val visitedScreens: Set<String> = setOf(),
+ val screens: Set<PreferenceScreenCoordinate> = setOf(),
+ val visitedScreens: Set<PreferenceScreenCoordinate> = setOf(),
val locale: Locale? = null,
val flags: Int = PreferenceGetterFlags.ALL,
val includeValueDescriptor: Boolean = true,
@@ -101,26 +102,32 @@ constructor(
object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
override fun encode(data: GetPreferenceGraphRequest): Bundle =
Bundle(4).apply {
- putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray())
- putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray())
+ putParcelableArray(KEY_SCREENS, data.screens.toTypedArray())
+ putParcelableArray(KEY_VISITED_SCREENS, data.visitedScreens.toTypedArray())
putString(KEY_LOCALE, data.locale?.toLanguageTag())
putInt(KEY_FLAGS, data.flags)
}
+ @Suppress("DEPRECATION")
override fun decode(data: Bundle): GetPreferenceGraphRequest {
- val screenKeys = data.getStringArray(KEY_SCREEN_KEYS) ?: arrayOf()
- val visitedScreens = data.getStringArray(KEY_VISITED_KEYS) ?: arrayOf()
+ data.classLoader = PreferenceScreenCoordinate::class.java.classLoader
+ val screens = data.getParcelableArray(KEY_SCREENS) ?: arrayOf()
+ val visitedScreens = data.getParcelableArray(KEY_VISITED_SCREENS) ?: arrayOf()
fun String?.toLocale() = if (this != null) Locale.forLanguageTag(this) else null
+ fun Array<Parcelable>.toScreenCoordinates() =
+ buildSet(size) {
+ for (element in this@toScreenCoordinates) add(element as PreferenceScreenCoordinate)
+ }
return GetPreferenceGraphRequest(
- screenKeys.toSet(),
- visitedScreens.toSet(),
+ screens.toScreenCoordinates(),
+ visitedScreens.toScreenCoordinates(),
data.getString(KEY_LOCALE).toLocale(),
data.getInt(KEY_FLAGS),
)
}
- private const val KEY_SCREEN_KEYS = "k"
- private const val KEY_VISITED_KEYS = "v"
+ private const val KEY_SCREENS = "s"
+ private const val KEY_VISITED_SCREENS = "v"
private const val KEY_LOCALE = "l"
private const val KEY_FLAGS = "f"
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
index a9958b975fc6..1d4e2c9e1bef 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -26,6 +26,7 @@ import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.metadata.PreferenceCoordinate
import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
import com.android.settingslib.metadata.PreferenceScreenRegistry
/**
@@ -105,8 +106,10 @@ class PreferenceGetterApiHandler(
val errors = mutableMapOf<PreferenceCoordinate, Int>()
val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
val flags = request.flags
- for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
- val screenMetadata = PreferenceScreenRegistry.create(application, screenKey)
+ val groups =
+ request.preferences.groupBy { PreferenceScreenCoordinate(it.screenKey, it.args) }
+ for ((screen, coordinates) in groups) {
+ val screenMetadata = PreferenceScreenRegistry.create(application, screen)
if (screenMetadata == null) {
val latencyMs = SystemClock.elapsedRealtime() - elapsedRealtime
for (coordinate in coordinates) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index c0d244989044..4290437b0d02 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -40,6 +40,7 @@ import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceProto.ActionTarget
import com.android.settingslib.graph.proto.PreferenceScreenProto
import com.android.settingslib.graph.proto.TextProto
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.IntRangeValuePreference
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
@@ -47,7 +48,10 @@ import com.android.settingslib.metadata.PreferenceHierarchy
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
+import com.android.settingslib.metadata.PreferenceScreenCoordinate
import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.PreferenceScreenMetadataFactory
+import com.android.settingslib.metadata.PreferenceScreenMetadataParameterizedFactory
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.metadata.PreferenceTitleProvider
@@ -72,15 +76,19 @@ private constructor(
PreferenceScreenFactory(context.ofLocale(request.locale))
}
private val builder by lazy { PreferenceGraphProto.newBuilder() }
- private val visitedScreens = mutableSetOf<String>().apply { addAll(request.visitedScreens) }
+ private val visitedScreens = request.visitedScreens.toMutableSet()
+ private val screens = mutableMapOf<String, PreferenceScreenProto.Builder>()
private suspend fun init() {
- for (key in request.screenKeys) {
- addPreferenceScreenFromRegistry(key)
+ for (screen in request.screens) {
+ PreferenceScreenRegistry.create(context, screen)?.let { addPreferenceScreen(it) }
}
}
- fun build(): PreferenceGraphProto = builder.build()
+ fun build(): PreferenceGraphProto {
+ for ((key, screenBuilder) in screens) builder.putScreens(key, screenBuilder.build())
+ return builder.build()
+ }
/**
* Adds an activity to the graph.
@@ -138,19 +146,12 @@ private constructor(
null
}
- suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
- val metadata = PreferenceScreenRegistry.create(context, key) ?: return false
- return addPreferenceScreenMetadata(metadata)
+ private suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
+ val factory =
+ PreferenceScreenRegistry.preferenceScreenMetadataFactories[key] ?: return false
+ return addPreferenceScreen(factory)
}
- private suspend fun addPreferenceScreenMetadata(metadata: PreferenceScreenMetadata): Boolean =
- addPreferenceScreen(metadata.key) {
- preferenceScreenProto {
- completeHierarchy = metadata.hasCompleteHierarchy()
- root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
- }
- }
-
suspend fun addPreferenceScreenProvider(activityClass: Class<*>) {
Log.d(TAG, "add $activityClass")
createPreferenceScreen { activityClass.newInstance() }
@@ -188,26 +189,52 @@ private constructor(
Log.e(TAG, "\"$preferenceScreen\" has no key")
return
}
- @Suppress("CheckReturnValue") addPreferenceScreen(key) { preferenceScreen.toProto(intent) }
+ val args = preferenceScreen.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+ @Suppress("CheckReturnValue")
+ addPreferenceScreen(key, args) {
+ this.intent = intent.toProto()
+ root = preferenceScreen.toProto()
+ }
+ }
+
+ suspend fun addPreferenceScreen(factory: PreferenceScreenMetadataFactory): Boolean {
+ if (factory is PreferenceScreenMetadataParameterizedFactory) {
+ factory.parameters(context).collect { addPreferenceScreen(factory.create(context, it)) }
+ return true
+ }
+ return addPreferenceScreen(factory.create(context))
}
+ private suspend fun addPreferenceScreen(metadata: PreferenceScreenMetadata): Boolean =
+ addPreferenceScreen(metadata.key, metadata.arguments) {
+ completeHierarchy = metadata.hasCompleteHierarchy()
+ root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
+ }
+
private suspend fun addPreferenceScreen(
key: String,
- preferenceScreenProvider: suspend () -> PreferenceScreenProto,
- ): Boolean =
- if (visitedScreens.add(key)) {
- builder.putScreens(key, preferenceScreenProvider())
- true
- } else {
- Log.w(TAG, "$key visited")
- false
+ args: Bundle?,
+ init: suspend PreferenceScreenProto.Builder.() -> Unit,
+ ): Boolean {
+ if (!visitedScreens.add(PreferenceScreenCoordinate(key, args))) {
+ Log.w(TAG, "$key $args visited")
+ return false
}
-
- private suspend fun PreferenceScreen.toProto(intent: Intent?): PreferenceScreenProto =
- preferenceScreenProto {
- intent?.let { this.intent = it.toProto() }
- root = (this@toProto as PreferenceGroup).toProto()
+ if (args == null) { // normal screen
+ screens[key] = PreferenceScreenProto.newBuilder().also { init(it) }
+ } else if (args.isEmpty) { // parameterized screen with backward compatibility
+ val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
+ init(builder)
+ } else { // parameterized screen with non-empty arguments
+ val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
+ val parameterizedScreen = parameterizedPreferenceScreenProto {
+ setArgs(args.toProto())
+ setScreen(PreferenceScreenProto.newBuilder().also { init(it) })
+ }
+ builder.addParameterizedScreens(parameterizedScreen)
}
+ return true
+ }
private suspend fun PreferenceGroup.toProto(): PreferenceGroupProto = preferenceGroupProto {
preference = (this@toProto as Preference).toProto()
@@ -271,7 +298,7 @@ private constructor(
.toProto(context, callingPid, callingUid, screenMetadata, isRoot, request.flags)
.also {
if (metadata is PreferenceScreenMetadata) {
- @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
+ @Suppress("CheckReturnValue") addPreferenceScreen(metadata)
}
metadata.intent(context)?.resolveActivity(context.packageManager)?.let {
if (it.packageName == context.packageName) {
@@ -322,7 +349,7 @@ private constructor(
val screenKey = screen?.key
if (!screenKey.isNullOrEmpty()) {
@Suppress("CheckReturnValue")
- addPreferenceScreen(screenKey) { screen.toProto(null) }
+ addPreferenceScreen(screenKey, null) { root = screen.toProto() }
return actionTargetProto { key = screenKey }
}
} catch (e: Exception) {
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index a595f42a573d..60f9c6bb92a3 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -40,11 +40,12 @@ import com.android.settingslib.metadata.SensitivityLevel.Companion.HIGH_SENSITIV
import com.android.settingslib.metadata.SensitivityLevel.Companion.UNKNOWN_SENSITIVITY
/** Request to set preference value. */
-data class PreferenceSetterRequest(
- val screenKey: String,
- val key: String,
+class PreferenceSetterRequest(
+ screenKey: String,
+ args: Bundle?,
+ key: String,
val value: PreferenceValueProto,
-)
+) : PreferenceCoordinate(screenKey, args, key)
/** Result of preference setter request. */
@IntDef(
@@ -121,7 +122,7 @@ class PreferenceSetterApiHandler(
metricsLogger?.logSetterApi(
application,
callingUid,
- PreferenceCoordinate(request.screenKey, request.key),
+ request,
null,
null,
PreferenceSetterResult.UNSUPPORTED,
@@ -130,7 +131,7 @@ class PreferenceSetterApiHandler(
return PreferenceSetterResult.UNSUPPORTED
}
val screenMetadata =
- PreferenceScreenRegistry.create(application, request.screenKey) ?: return notFound()
+ PreferenceScreenRegistry.create(application, request) ?: return notFound()
val key = request.key
val metadata =
screenMetadata.getPreferenceHierarchy(application).find(key) ?: return notFound()
@@ -199,7 +200,7 @@ class PreferenceSetterApiHandler(
metricsLogger?.logSetterApi(
application,
callingUid,
- PreferenceCoordinate(request.screenKey, request.key),
+ request,
screenMetadata,
metadata,
result,
@@ -235,6 +236,7 @@ object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
override fun encode(data: PreferenceSetterRequest) =
Bundle(3).apply {
putString(SCREEN_KEY, data.screenKey)
+ putBundle(ARGS, data.args)
putString(KEY, data.key)
putByteArray(null, data.value.toByteArray())
}
@@ -242,10 +244,12 @@ object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
override fun decode(data: Bundle) =
PreferenceSetterRequest(
data.getString(SCREEN_KEY)!!,
+ data.getBundle(ARGS),
data.getString(KEY)!!,
PreferenceValueProto.parseFrom(data.getByteArray(null)!!),
)
private const val SCREEN_KEY = "s"
private const val KEY = "k"
+ private const val ARGS = "a"
}
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
index adbe77318353..5f2a0d826407 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/ProtoDsl.kt
@@ -19,6 +19,7 @@ package com.android.settingslib.graph
import com.android.settingslib.graph.proto.BundleProto
import com.android.settingslib.graph.proto.BundleProto.BundleValue
import com.android.settingslib.graph.proto.IntentProto
+import com.android.settingslib.graph.proto.ParameterizedPreferenceScreenProto
import com.android.settingslib.graph.proto.PreferenceGroupProto
import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.proto.PreferenceProto
@@ -39,6 +40,12 @@ inline fun preferenceScreenProto(
init: PreferenceScreenProto.Builder.() -> Unit
): PreferenceScreenProto = PreferenceScreenProto.newBuilder().also(init).build()
+/** Kotlin DSL-style builder for [PreferenceScreenProto]. */
+inline fun parameterizedPreferenceScreenProto(
+ init: ParameterizedPreferenceScreenProto.Builder.() -> Unit
+): ParameterizedPreferenceScreenProto =
+ ParameterizedPreferenceScreenProto.newBuilder().also(init).build()
+
/** Returns preference or null. */
val PreferenceOrGroupProto.preferenceOrNull
get() = if (hasPreference()) preference else null
diff --git a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
index 38b641336547..69b75adea9d3 100644
--- a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -33,6 +33,9 @@ import javax.tools.Diagnostic
/** Processor to gather preference screens annotated with `@ProvidePreferenceScreen`. */
class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
private val screens = mutableListOf<Screen>()
+ private val bundleType: TypeMirror by lazy {
+ processingEnv.elementUtils.getTypeElement("android.os.Bundle").asType()
+ }
private val contextType: TypeMirror by lazy {
processingEnv.elementUtils.getTypeElement("android.content.Context").asType()
}
@@ -83,19 +86,57 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
error("@$ANNOTATION_NAME must be added to $PREFERENCE_SCREEN_METADATA subclass", this)
return
}
- val constructorType = getConstructorType()
- if (constructorType == null) {
+ fun reportConstructorError() =
error(
- "Class must be an object, or has single public constructor that " +
- "accepts no parameter or a Context parameter",
+ "Must have only one public constructor: constructor(), " +
+ "constructor(Context), constructor(Bundle) or constructor(Context, Bundle)",
this,
)
+ val constructor = findConstructor()
+ if (constructor == null || constructor.parameters.size > 2) {
+ reportConstructorError()
return
}
+ val constructorHasContextParameter = constructor.hasParameter(0, contextType)
+ var index = if (constructorHasContextParameter) 1 else 0
val annotation = annotationMirrors.single { it.isElement(annotationElement) }
val key = annotation.fieldValue<String>("value")!!
val overlay = annotation.fieldValue<Boolean>("overlay") == true
- screens.add(Screen(key, overlay, qualifiedName.toString(), constructorType))
+ val parameterized = annotation.fieldValue<Boolean>("parameterized") == true
+ var parametersHasContextParameter = false
+ if (parameterized) {
+ val parameters = findParameters()
+ if (parameters == null) {
+ error("require a static 'parameters()' or 'parameters(Context)' method", this)
+ return
+ }
+ parametersHasContextParameter = parameters
+ if (constructor.hasParameter(index, bundleType)) {
+ index++
+ } else {
+ error(
+ "Parameterized screen constructor must be" +
+ "constructor(Bundle) or constructor(Context, Bundle)",
+ this,
+ )
+ return
+ }
+ }
+ if (index == constructor.parameters.size) {
+ screens.add(
+ Screen(
+ key,
+ overlay,
+ parameterized,
+ annotation.fieldValue<Boolean>("parameterizedMigration") == true,
+ qualifiedName.toString(),
+ constructorHasContextParameter,
+ parametersHasContextParameter,
+ )
+ )
+ } else {
+ reportConstructorError()
+ }
}
private fun codegen() {
@@ -116,10 +157,15 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
screens.sort()
processingEnv.filer.createSourceFile("$outputPkg.$outputClass").openWriter().use {
it.write("package $outputPkg;\n\n")
+ it.write("import android.content.Context;\n")
+ it.write("import android.os.Bundle;\n")
it.write("import $PACKAGE.FixedArrayMap;\n")
it.write("import $PACKAGE.FixedArrayMap.OrderedInitializer;\n")
- it.write("import $PACKAGE.$FACTORY;\n\n")
- it.write("// Generated by annotation processor for @$ANNOTATION_NAME\n")
+ it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n")
+ it.write("import $PACKAGE.$FACTORY;\n")
+ it.write("import $PACKAGE.$PARAMETERIZED_FACTORY;\n")
+ it.write("import kotlinx.coroutines.flow.Flow;\n")
+ it.write("\n// Generated by annotation processor for @$ANNOTATION_NAME\n")
it.write("public final class $outputClass {\n")
it.write(" private $outputClass() {}\n\n")
it.write(" public static FixedArrayMap<String, $FACTORY> $outputFun() {\n")
@@ -127,10 +173,29 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
it.write(" return new FixedArrayMap<>($size, $outputClass::init);\n")
it.write(" }\n\n")
fun Screen.write() {
- it.write(" screens.put(\"$key\", context -> new $klass(")
- when (constructorType) {
- ConstructorType.DEFAULT -> it.write("));")
- ConstructorType.CONTEXT -> it.write("context));")
+ it.write(" screens.put(\"$key\", ")
+ if (parameterized) {
+ it.write("new $PARAMETERIZED_FACTORY() {\n")
+ it.write(" @Override public PreferenceScreenMetadata create")
+ it.write("(Context context, Bundle args) {\n")
+ it.write(" return new $klass(")
+ if (constructorHasContextParameter) it.write("context, ")
+ it.write("args);\n")
+ it.write(" }\n\n")
+ it.write(" @Override public Flow<Bundle> parameters(Context context) {\n")
+ it.write(" return $klass.parameters(")
+ if (parametersHasContextParameter) it.write("context")
+ it.write(");\n")
+ it.write(" }\n")
+ if (parameterizedMigration) {
+ it.write("\n @Override public boolean acceptEmptyArguments()")
+ it.write(" { return true; }\n")
+ }
+ it.write(" });")
+ } else {
+ it.write("context -> new $klass(")
+ if (constructorHasContextParameter) it.write("context")
+ it.write("));")
}
if (overlay) it.write(" // overlay")
it.write("\n")
@@ -159,7 +224,7 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
private fun AnnotationMirror.isElement(element: TypeElement) =
- processingEnv.typeUtils.isSameType(annotationType.asElement().asType(), element.asType())
+ annotationType.asElement().asType().isSameType(element.asType())
@Suppress("UNCHECKED_CAST")
private fun <T> AnnotationMirror.fieldValue(name: String): T? = field(name)?.value as? T
@@ -171,7 +236,7 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
return null
}
- private fun TypeElement.getConstructorType(): ConstructorType? {
+ private fun TypeElement.findConstructor(): ExecutableElement? {
var constructor: ExecutableElement? = null
for (element in enclosedElements) {
if (element.kind != ElementKind.CONSTRUCTOR) continue
@@ -179,16 +244,30 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
if (constructor != null) return null
constructor = element as ExecutableElement
}
- return constructor?.parameters?.run {
- when {
- isEmpty() -> ConstructorType.DEFAULT
- size == 1 && processingEnv.typeUtils.isSameType(this[0].asType(), contextType) ->
- ConstructorType.CONTEXT
- else -> null
- }
+ return constructor
+ }
+
+ private fun TypeElement.findParameters(): Boolean? {
+ for (element in enclosedElements) {
+ if (element.kind != ElementKind.METHOD) continue
+ if (!element.modifiers.contains(Modifier.PUBLIC)) continue
+ if (!element.modifiers.contains(Modifier.STATIC)) continue
+ if (!element.simpleName.contentEquals("parameters")) return null
+ val parameters = (element as ExecutableElement).parameters
+ if (parameters.isEmpty()) return false
+ if (parameters.size == 1 && parameters[0].asType().isSameType(contextType)) return true
+ error("parameters method should have no parameter or a Context parameter", element)
+ return null
}
+ return null
}
+ private fun ExecutableElement.hasParameter(index: Int, typeMirror: TypeMirror) =
+ index < parameters.size && parameters[index].asType().isSameType(typeMirror)
+
+ private fun TypeMirror.isSameType(typeMirror: TypeMirror) =
+ processingEnv.typeUtils.isSameType(this, typeMirror)
+
private fun warn(msg: CharSequence) =
processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, msg)
@@ -198,8 +277,11 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
private data class Screen(
val key: String,
val overlay: Boolean,
+ val parameterized: Boolean,
+ val parameterizedMigration: Boolean,
val klass: String,
- val constructorType: ConstructorType,
+ val constructorHasContextParameter: Boolean,
+ val parametersHasContextParameter: Boolean,
) : Comparable<Screen> {
override fun compareTo(other: Screen): Int {
val diff = key.compareTo(other.key)
@@ -207,17 +289,13 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
}
- private enum class ConstructorType {
- DEFAULT, // default constructor with no parameter
- CONTEXT, // constructor with a Context parameter
- }
-
companion object {
private const val PACKAGE = "com.android.settingslib.metadata"
private const val ANNOTATION_NAME = "ProvidePreferenceScreen"
private const val ANNOTATION = "$PACKAGE.$ANNOTATION_NAME"
private const val PREFERENCE_SCREEN_METADATA = "PreferenceScreenMetadata"
private const val FACTORY = "PreferenceScreenMetadataFactory"
+ private const val PARAMETERIZED_FACTORY = "PreferenceScreenMetadataParameterizedFactory"
private const val OPTIONS_NAME = "ProvidePreferenceScreenOptions"
private const val OPTIONS = "$PACKAGE.$OPTIONS_NAME"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
index 4bed795ea760..449c78ce8965 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
@@ -22,14 +22,27 @@ package com.android.settingslib.metadata
* The annotated class must satisfy either condition:
* - the primary constructor has no parameter
* - the primary constructor has a single [android.content.Context] parameter
+ * - (parameterized) the primary constructor has a single [android.os.Bundle] parameter to override
+ * [PreferenceScreenMetadata.arguments]
+ * - (parameterized) the primary constructor has a [android.content.Context] and a
+ * [android.os.Bundle] parameter to override [PreferenceScreenMetadata.arguments]
*
* @param value unique preference screen key
* @param overlay if true, current annotated screen will overlay the screen that has identical key
+ * @param parameterized if true, the screen relies on additional arguments to build its content
+ * @param parameterizedMigration whether the parameterized screen was a normal screen, in which case
+ * `Bundle.EMPTY` will be passed as arguments to take care of backward compatibility
+ * @see PreferenceScreenMetadata
*/
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
-annotation class ProvidePreferenceScreen(val value: String, val overlay: Boolean = false)
+annotation class ProvidePreferenceScreen(
+ val value: String,
+ val overlay: Boolean = false,
+ val parameterized: Boolean = false,
+ val parameterizedMigration: Boolean = false, // effective only when parameterized is true
+)
/**
* Provides options for [ProvidePreferenceScreen] annotation processor.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index 876f6152cccd..3bd051dee41d 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.metadata
import android.content.Context
+import android.os.Bundle
/** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata) {
@@ -54,8 +55,14 @@ internal constructor(private val context: Context, metadata: PreferenceMetadata)
*
* @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
*/
- operator fun String.unaryPlus() =
- +PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, this)!!)
+ operator fun String.unaryPlus() = addPreferenceScreen(this, null)
+
+ /**
+ * Adds parameterized preference screen with given key (as a placeholder) to the hierarchy.
+ *
+ * @see String.unaryPlus
+ */
+ infix fun String.args(args: Bundle) = createPreferenceScreenHierarchy(this, args)
operator fun PreferenceHierarchyNode.unaryPlus() = also { children.add(it) }
@@ -122,6 +129,14 @@ internal constructor(private val context: Context, metadata: PreferenceMetadata)
}
/**
+ * Adds parameterized preference screen with given key (as a placeholder) to the hierarchy.
+ *
+ * @see addPreferenceScreen
+ */
+ fun addParameterizedScreen(screenKey: String, args: Bundle) =
+ addPreferenceScreen(screenKey, args)
+
+ /**
* Adds preference screen with given key (as a placeholder) to the hierarchy.
*
* This is mainly to support Android Settings overlays. OEMs might want to custom some of the
@@ -132,11 +147,13 @@ internal constructor(private val context: Context, metadata: PreferenceMetadata)
*
* @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
*/
- fun addPreferenceScreen(screenKey: String) {
- children.add(
- PreferenceHierarchy(context, PreferenceScreenRegistry.create(context, screenKey)!!)
- )
- }
+ fun addPreferenceScreen(screenKey: String) = addPreferenceScreen(screenKey, null)
+
+ private fun addPreferenceScreen(screenKey: String, args: Bundle?): PreferenceHierarchyNode =
+ createPreferenceScreenHierarchy(screenKey, args).also { children.add(it) }
+
+ private fun createPreferenceScreenHierarchy(screenKey: String, args: Bundle?) =
+ PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, screenKey, args)!!)
/** Extensions to add more preferences to the hierarchy. */
operator fun PreferenceHierarchy.plusAssign(init: PreferenceHierarchy.() -> Unit) = init(this)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
index 84014f191f68..4fd13ede6803 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenBindingKeyProvider.kt
@@ -17,13 +17,20 @@
package com.android.settingslib.metadata
import android.content.Context
+import android.os.Bundle
/** Provides the associated preference screen key for binding. */
interface PreferenceScreenBindingKeyProvider {
/** Returns the associated preference screen key. */
fun getPreferenceScreenBindingKey(context: Context): String?
+
+ /** Returns the arguments to build preference screen. */
+ fun getPreferenceScreenBindingArgs(context: Context): Bundle?
}
/** Extra key to provide the preference screen key for binding. */
const val EXTRA_BINDING_SCREEN_KEY = "settingslib:binding_screen_key"
+
+/** Extra key to provide arguments for preference screen binding. */
+const val EXTRA_BINDING_SCREEN_ARGS = "settingslib:binding_screen_args"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
index 850d4523e96e..7f1ded71e30a 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
@@ -18,12 +18,25 @@ package com.android.settingslib.metadata
import android.content.Context
import android.content.Intent
+import android.os.Bundle
import androidx.annotation.AnyThread
import androidx.fragment.app.Fragment
+import kotlinx.coroutines.flow.Flow
-/** Metadata of preference screen. */
+/**
+ * Metadata of preference screen.
+ *
+ * For parameterized preference screen that relies on additional information (e.g. package name,
+ * language code) to build its content, the subclass must:
+ * - override [arguments] in constructor
+ * - add a static method `fun parameters(context: Context): List<Bundle>` (context is optional) to
+ * provide all possible arguments
+ */
@AnyThread
interface PreferenceScreenMetadata : PreferenceMetadata {
+ /** Arguments to build the screen content. */
+ val arguments: Bundle?
+ get() = null
/**
* The screen title resource, which precedes [getScreenTitle] if provided.
@@ -65,7 +78,12 @@ interface PreferenceScreenMetadata : PreferenceMetadata {
fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
}
-/** Factory of [PreferenceScreenMetadata]. */
+/**
+ * Factory of [PreferenceScreenMetadata].
+ *
+ * Annotation processor generates implementation of this interface based on
+ * [ProvidePreferenceScreen] when [ProvidePreferenceScreen.parameterized] is `false`.
+ */
fun interface PreferenceScreenMetadataFactory {
/**
@@ -75,3 +93,44 @@ fun interface PreferenceScreenMetadataFactory {
*/
fun create(context: Context): PreferenceScreenMetadata
}
+
+/**
+ * Parameterized factory of [PreferenceScreenMetadata].
+ *
+ * Annotation processor generates implementation of this interface based on
+ * [ProvidePreferenceScreen] when [ProvidePreferenceScreen.parameterized] is `true`.
+ */
+interface PreferenceScreenMetadataParameterizedFactory : PreferenceScreenMetadataFactory {
+ override fun create(context: Context) = create(context, Bundle.EMPTY)
+
+ /**
+ * Creates a new [PreferenceScreenMetadata] with given arguments.
+ *
+ * @param context application context to create the PreferenceScreenMetadata
+ * @param args arguments to create the screen metadata, [Bundle.EMPTY] is reserved for the
+ * default case when screen is migrated from normal to parameterized
+ */
+ fun create(context: Context, args: Bundle): PreferenceScreenMetadata
+
+ /**
+ * Returns all possible arguments to create [PreferenceScreenMetadata].
+ *
+ * Note that [Bundle.EMPTY] is a special arguments reserved for backward compatibility when a
+ * preference screen was a normal screen but migrated to parameterized screen later:
+ * 1. Set [ProvidePreferenceScreen.parameterizedMigration] to `true`, so that the generated
+ * [acceptEmptyArguments] will be `true`.
+ * 1. In the original [parameters] implementation, produce a [Bundle.EMPTY] for the default
+ * case.
+ *
+ * Do not use [Bundle.EMPTY] for other purpose.
+ */
+ fun parameters(context: Context): Flow<Bundle>
+
+ /**
+ * Returns true when the parameterized screen was a normal screen.
+ *
+ * The [PreferenceScreenMetadata] is expected to accept an empty arguments ([Bundle.EMPTY]) and
+ * take care of backward compatibility.
+ */
+ fun acceptEmptyArguments(): Boolean = false
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index c74b3151abb2..246310984db9 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -17,10 +17,13 @@
package com.android.settingslib.metadata
import android.content.Context
+import android.os.Bundle
+import android.util.Log
import com.android.settingslib.datastore.KeyValueStore
/** Registry of all available preference screens in the app. */
object PreferenceScreenRegistry : ReadWritePermitProvider {
+ private const val TAG = "ScreenRegistry"
/** Provider of key-value store. */
private lateinit var keyValueStoreProvider: KeyValueStoreProvider
@@ -52,9 +55,28 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore? =
keyValueStoreProvider.getKeyValueStore(context, preference)
- /** Creates [PreferenceScreenMetadata] of particular screen key. */
- fun create(context: Context, screenKey: String?): PreferenceScreenMetadata? =
- screenKey?.let { preferenceScreenMetadataFactories[it]?.create(context.applicationContext) }
+ /** Creates [PreferenceScreenMetadata] of particular screen. */
+ fun create(context: Context, screenCoordinate: PreferenceScreenCoordinate) =
+ create(context, screenCoordinate.screenKey, screenCoordinate.args)
+
+ /** Creates [PreferenceScreenMetadata] of particular screen key with given arguments. */
+ fun create(context: Context, screenKey: String?, args: Bundle?): PreferenceScreenMetadata? {
+ if (screenKey == null) return null
+ val factory = preferenceScreenMetadataFactories[screenKey] ?: return null
+ val appContext = context.applicationContext
+ if (factory is PreferenceScreenMetadataParameterizedFactory) {
+ if (args != null) return factory.create(appContext, args)
+ // In case the parameterized screen was a normal scree, it is expected to accept
+ // Bundle.EMPTY arguments and take care of backward compatibility.
+ if (factory.acceptEmptyArguments()) return factory.create(appContext)
+ Log.e(TAG, "screen $screenKey is parameterized but args is not provided")
+ return null
+ } else {
+ if (args == null) return factory.create(appContext)
+ Log.e(TAG, "screen $screenKey is not parameterized but args is provided")
+ return null
+ }
+ }
/**
* Sets the provider to check read write permit. Read and write requests are denied by default.
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index 65fbe2b66e77..dbac17d4e8b8 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -22,6 +22,7 @@ import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
@@ -35,9 +36,11 @@ interface PreferenceScreenBinding : PreferenceBinding {
super.bind(preference, metadata)
val context = preference.context
val screenMetadata = metadata as PreferenceScreenMetadata
+ val extras = preference.extras
// Pass the preference key to fragment, so that the fragment could find associated
// preference screen registered in PreferenceScreenRegistry
- preference.extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+ extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+ screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
if (preference is PreferenceScreen) {
val screenTitle = screenMetadata.screenTitle
preference.title =
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index ffe181d0c350..02f91c1bb50b 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -23,6 +23,7 @@ import android.util.Log
import androidx.annotation.XmlRes
import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider
import com.android.settingslib.metadata.PreferenceScreenRegistry
@@ -89,13 +90,19 @@ open class PreferenceFragment :
@XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0
protected fun getPreferenceScreenCreator(context: Context): PreferenceScreenCreator? =
- (PreferenceScreenRegistry.create(context, getPreferenceScreenBindingKey(context))
- as? PreferenceScreenCreator)
+ (PreferenceScreenRegistry.create(
+ context,
+ getPreferenceScreenBindingKey(context),
+ getPreferenceScreenBindingArgs(context),
+ ) as? PreferenceScreenCreator)
?.run { if (isFlagEnabled(context)) this else null }
override fun getPreferenceScreenBindingKey(context: Context): String? =
arguments?.getString(EXTRA_BINDING_SCREEN_KEY)
+ override fun getPreferenceScreenBindingArgs(context: Context): Bundle? =
+ arguments?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
preferenceScreenBindingHelper?.onCreate()
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 4a6a589cd3c9..1cb8005ddae0 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -31,6 +31,7 @@ import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedDataObservable
import com.android.settingslib.datastore.KeyedObservable
import com.android.settingslib.datastore.KeyedObserver
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceChangeReason
import com.android.settingslib.metadata.PreferenceHierarchy
@@ -227,14 +228,16 @@ class PreferenceScreenBindingHelper(
/** Updates preference screen that has incomplete hierarchy. */
@JvmStatic
fun bind(preferenceScreen: PreferenceScreen) {
- PreferenceScreenRegistry.create(preferenceScreen.context, preferenceScreen.key)?.run {
+ val context = preferenceScreen.context
+ val args = preferenceScreen.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
+ PreferenceScreenRegistry.create(context, preferenceScreen.key, args)?.run {
if (!hasCompleteHierarchy()) {
val preferenceBindingFactory =
(this as? PreferenceScreenCreator)?.preferenceBindingFactory ?: return
bindRecursively(
preferenceScreen,
preferenceBindingFactory,
- getPreferenceHierarchy(preferenceScreen.context),
+ getPreferenceHierarchy(context),
)
}
}
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
index 211b3bdaea70..88c4fe6bf188 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
@@ -17,10 +17,12 @@
package com.android.settingslib.preference
import android.content.Context
+import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.PreferenceScreenRegistry
/** Factory to create preference screen. */
@@ -81,8 +83,12 @@ class PreferenceScreenFactory {
*
* The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
*/
- fun createBindingScreen(context: Context, screenKey: String?): PreferenceScreen? {
- val metadata = PreferenceScreenRegistry.create(context, screenKey) ?: return null
+ fun createBindingScreen(
+ context: Context,
+ screenKey: String?,
+ args: Bundle?,
+ ): PreferenceScreen? {
+ val metadata = PreferenceScreenRegistry.create(context, screenKey, args) ?: return null
if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
return metadata.createPreferenceScreen(this)
}
@@ -94,8 +100,9 @@ class PreferenceScreenFactory {
@JvmStatic
fun createBindingScreen(preference: Preference): PreferenceScreen? {
val context = preference.context
+ val args = preference.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
val preferenceScreenCreator =
- (PreferenceScreenRegistry.create(context, preference.key)
+ (PreferenceScreenRegistry.create(context, preference.key, args)
as? PreferenceScreenCreator) ?: return null
if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
val factory = PreferenceScreenFactory(context)