summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt4
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt2
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt2
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt3
-rw-r--r--packages/SettingsLib/Metadata/Android.bp3
-rw-r--r--packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt112
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt10
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.kt161
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt28
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt11
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt40
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt2
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt2
-rw-r--r--packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt11
14 files changed, 274 insertions, 117 deletions
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 3c8d6ed0bf55..19fa5078fd5b 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -44,8 +44,8 @@ abstract class GetPreferenceGraphApiHandler(
): PreferenceGraphProto {
val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
if (request.screenKeys.isEmpty()) {
- for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
- builder.addPreferenceScreenFromRegistry(key)
+ PreferenceScreenRegistry.preferenceScreenMetadataCreators.forEachKeyAsync {
+ builder.addPreferenceScreenFromRegistry(it)
}
for (provider in preferenceScreenProviders) {
builder.addPreferenceScreenProvider(provider)
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 de5731eacfd7..2fac54557bef 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -98,7 +98,7 @@ class PreferenceGetterApiHandler(
val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
val flags = request.flags
for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
- val screenMetadata = PreferenceScreenRegistry[screenKey]
+ val screenMetadata = PreferenceScreenRegistry.create(application, screenKey)
if (screenMetadata == null) {
for (coordinate in coordinates) {
errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND
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 2cf32decd5e2..f001fad69dc6 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -141,7 +141,7 @@ private constructor(
}
suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
- val metadata = PreferenceScreenRegistry[key] ?: return false
+ val metadata = PreferenceScreenRegistry.create(context, key) ?: return false
return addPreferenceScreenMetadata(metadata)
}
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 bef4bb297654..3c870acf2291 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -112,7 +112,8 @@ class PreferenceSetterApiHandler(
request: PreferenceSetterRequest,
): Int {
val screenMetadata =
- PreferenceScreenRegistry[request.screenKey] ?: return PreferenceSetterResult.UNSUPPORTED
+ PreferenceScreenRegistry.create(application, request.screenKey)
+ ?: return PreferenceSetterResult.UNSUPPORTED
val key = request.key
val metadata =
screenMetadata.getPreferenceHierarchy(application).find(key)
diff --git a/packages/SettingsLib/Metadata/Android.bp b/packages/SettingsLib/Metadata/Android.bp
index 207637f86372..564c3985264d 100644
--- a/packages/SettingsLib/Metadata/Android.bp
+++ b/packages/SettingsLib/Metadata/Android.bp
@@ -14,10 +14,9 @@ android_library {
],
srcs: [":SettingsLibMetadata-srcs"],
static_libs: [
+ "SettingsLibDataStore",
"androidx.annotation_annotation",
"androidx.fragment_fragment",
- "guava",
- "SettingsLibDataStore",
],
kotlincflags: ["-Xjvm-default=all"],
}
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 7432254b57a4..1049cceb4f76 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
@@ -16,7 +16,6 @@
package com.android.settingslib.metadata
-import java.util.TreeMap
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
@@ -33,8 +32,7 @@ import javax.tools.Diagnostic
/** Processor to gather preference screens annotated with `@ProvidePreferenceScreen`. */
class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
- private val screens = TreeMap<String, ConstructorType>()
- private val overlays = mutableMapOf<String, String>()
+ private val screens = mutableListOf<Screen>()
private val contextType: TypeMirror by lazy {
processingEnv.elementUtils.getTypeElement("android.content.Context").asType()
}
@@ -94,15 +92,10 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
)
return
}
- val screenQualifiedName = qualifiedName.toString()
- screens[screenQualifiedName] = constructorType
val annotation = annotationMirrors.single { it.isElement(annotationElement) }
- val overlay = annotation.getOverlay()
- if (overlay != null) {
- overlays.put(overlay, screenQualifiedName)?.let {
- error("$overlay has been overlaid by $it", this)
- }
- }
+ val key = annotation.fieldValue<String>("value")!!
+ val overlay = annotation.fieldValue<Boolean>("overlay") == true
+ screens.add(Screen(key, overlay, qualifiedName.toString(), constructorType))
}
private fun codegen() {
@@ -119,16 +112,8 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
private fun generateCode(outputPkg: String, outputClass: String, outputFun: String) {
- for ((overlay, screen) in overlays) {
- if (screens.remove(overlay) == null) {
- warn("$overlay is overlaid by $screen but not annotated with @$ANNOTATION_NAME")
- } else {
- processingEnv.messager.printMessage(
- Diagnostic.Kind.NOTE,
- "$overlay is overlaid by $screen",
- )
- }
- }
+ // sort by screen keys to make the output deterministic and naturally fit to FixedArrayMap
+ screens.sort()
val javaFileObject =
try {
processingEnv.filer.createSourceFile("$outputPkg.$outputClass")
@@ -139,56 +124,64 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
javaFileObject.openWriter().use {
it.write("package $outputPkg;\n\n")
- it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n\n")
+ it.write("import $PACKAGE.FixedArrayMap;\n")
+ it.write("import $PACKAGE.FixedArrayMap.OrderedInitializer;\n")
+ it.write("import $PACKAGE.$CREATOR;\n\n")
it.write("// 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 java.util.List<$PREFERENCE_SCREEN_METADATA> " +
- "$outputFun(android.content.Context context) {\n"
- )
- it.write(
- " java.util.ArrayList<$PREFERENCE_SCREEN_METADATA> screens = " +
- "new java.util.ArrayList<>(${screens.size});\n"
- )
- for ((screen, constructorType) in screens) {
+ it.write(" public static FixedArrayMap<String, $CREATOR> $outputFun() {\n")
+ val size = screens.size
+ 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(" screens.add(new $screen());\n")
- ConstructorType.CONTEXT -> it.write(" screens.add(new $screen(context));\n")
- ConstructorType.SINGLETON -> it.write(" screens.add($screen.INSTANCE);\n")
+ ConstructorType.DEFAULT -> it.write("));")
+ ConstructorType.CONTEXT -> it.write("context));")
}
+ if (overlay) it.write(" // overlay")
+ it.write("\n")
}
- for ((overlay, screen) in overlays) {
- it.write(" // $overlay is overlaid by $screen\n")
+ it.write(" private static void init(OrderedInitializer<String, $CREATOR> screens) {\n")
+ var index = 0
+ while (index < size) {
+ val screen = screens[index]
+ var next = index + 1
+ while (next < size && screen.key == screens[next].key) next++
+ val n = next - index
+ if (n == 1) {
+ screen.write()
+ } else if (n == 2 && screen.overlay && !screens[index + 1].overlay) {
+ it.write(" // ${screen.klass} overlays ${screens[index + 1].klass}\n")
+ screen.write()
+ } else {
+ val msg = StringBuilder("${screen.key} is associated to")
+ for (i in index until next) msg.append(" ${screens[i]}")
+ processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg)
+ }
+ index = next
}
- it.write(" return screens;\n")
- it.write(" }\n")
- it.write("}")
+ it.write(" }\n}")
}
}
private fun AnnotationMirror.isElement(element: TypeElement) =
processingEnv.typeUtils.isSameType(annotationType.asElement().asType(), element.asType())
- private fun AnnotationMirror.getOverlay(): String? {
+ @Suppress("UNCHECKED_CAST")
+ private fun <T> AnnotationMirror.fieldValue(name: String): T? = field(name)?.value as? T
+
+ private fun AnnotationMirror.field(name: String): AnnotationValue? {
for ((key, value) in elementValues) {
- if (key.simpleName.contentEquals("overlay")) {
- return if (value.isDefaultClassValue(key)) null else value.value.toString()
- }
+ if (key.simpleName.contentEquals(name)) return value
}
return null
}
- private fun AnnotationValue.isDefaultClassValue(key: ExecutableElement) =
- processingEnv.typeUtils.isSameType(
- value as TypeMirror,
- key.defaultValue.value as TypeMirror,
- )
-
private fun TypeElement.getConstructorType(): ConstructorType? {
var constructor: ExecutableElement? = null
for (element in enclosedElements) {
- if (element.isKotlinObject()) return ConstructorType.SINGLETON
if (element.kind != ElementKind.CONSTRUCTOR) continue
if (!element.modifiers.contains(Modifier.PUBLIC)) continue
if (constructor != null) return null
@@ -204,21 +197,27 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
}
}
- private fun Element.isKotlinObject() =
- kind == ElementKind.FIELD &&
- modifiers.run { contains(Modifier.PUBLIC) && contains(Modifier.STATIC) } &&
- simpleName.toString() == "INSTANCE"
-
private fun warn(msg: CharSequence) =
processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, msg)
private fun error(msg: CharSequence, element: Element) =
processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg, element)
+ private data class Screen(
+ val key: String,
+ val overlay: Boolean,
+ val klass: String,
+ val constructorType: ConstructorType,
+ ) : Comparable<Screen> {
+ override fun compareTo(other: Screen): Int {
+ val diff = key.compareTo(other.key)
+ return if (diff != 0) diff else other.overlay.compareTo(overlay)
+ }
+ }
+
private enum class ConstructorType {
DEFAULT, // default constructor with no parameter
CONTEXT, // constructor with a Context parameter
- SINGLETON, // Kotlin object class
}
companion object {
@@ -226,6 +225,7 @@ class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
private const val ANNOTATION_NAME = "ProvidePreferenceScreen"
private const val ANNOTATION = "$PACKAGE.$ANNOTATION_NAME"
private const val PREFERENCE_SCREEN_METADATA = "PreferenceScreenMetadata"
+ private const val CREATOR = "PreferenceScreenMetadataCreator"
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 ea20a74de3cf..4bed795ea760 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
@@ -16,24 +16,20 @@
package com.android.settingslib.metadata
-import kotlin.reflect.KClass
-
/**
* Annotation to provide preference screen.
*
* The annotated class must satisfy either condition:
* - the primary constructor has no parameter
* - the primary constructor has a single [android.content.Context] parameter
- * - it is a Kotlin object class
*
- * @param overlay if specified, current annotated screen will overlay the given screen
+ * @param value unique preference screen key
+ * @param overlay if true, current annotated screen will overlay the screen that has identical key
*/
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
@MustBeDocumented
-annotation class ProvidePreferenceScreen(
- val overlay: KClass<out PreferenceScreenMetadata> = PreferenceScreenMetadata::class,
-)
+annotation class ProvidePreferenceScreen(val value: String, val overlay: Boolean = false)
/**
* Provides options for [ProvidePreferenceScreen] annotation processor.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.kt
new file mode 100644
index 000000000000..149331a5947a
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024 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.settingslib.metadata
+
+import java.util.function.Consumer
+
+/**
+ * A compact and immutable data structure provides [get] and for-each operations on ordered
+ * key-value entries.
+ *
+ * The implementation uses fixed-size array and no API is offered to modify entries. Actually,
+ * elements are provided in sorted order during constructor to simplify data management. As a
+ * result, this class is more lightweight compared with `ArrayMap`.
+ */
+@Suppress("UNCHECKED_CAST")
+class FixedArrayMap<K : Comparable<K>, V> {
+ private val array: Array<Any?>
+
+ /** Constructors with empty element. */
+ constructor() {
+ array = emptyArray()
+ }
+
+ /**
+ * Constructors.
+ *
+ * @param size the number of elements
+ * @param consumer initializer to provide exactly [size] elements *in sorted order*
+ */
+ constructor(size: Int, consumer: Consumer<OrderedInitializer<K, V>>) {
+ array = arrayOfNulls(size * 2)
+ val orderedInitializer = OrderedInitializer<K, V>(array)
+ consumer.accept(orderedInitializer)
+ orderedInitializer.verify()
+ }
+
+ /** Returns the number of elements. */
+ val size: Int
+ get() = array.size / 2
+
+ /**
+ * Returns a new [FixedArrayMap] that merged from current and given [FixedArrayMap] instance.
+ *
+ * [other] takes precedence for identical keys.
+ */
+ fun merge(other: FixedArrayMap<K, V>): FixedArrayMap<K, V> {
+ var newKeys = 0
+ other.forEachKey { if (get(it) == null) newKeys++ }
+ return FixedArrayMap(size + newKeys) { initializer ->
+ var index1 = 0
+ var index2 = 0
+ while (!initializer.isDone()) {
+ val key1 = if (index1 < array.size) array[index1] as K else null
+ val key2 = if (index2 < other.array.size) other.array[index2] as K else null
+ val diff =
+ when {
+ key1 == null -> 1
+ key2 == null -> -1
+ else -> key1.compareTo(key2)
+ }
+ if (diff < 0) {
+ initializer.put(key1!!, array[index1 + 1] as V)
+ index1 += 2
+ } else {
+ initializer.put(key2!!, other.array[index2 + 1] as V)
+ index2 += 2
+ if (diff == 0) index1 += 2
+ }
+ }
+ }
+ }
+
+ /** Traversals keys *in sorted order* and applies given action. */
+ fun forEachKey(action: (key: K) -> Unit) {
+ for (index in array.indices step 2) {
+ action(array[index] as K)
+ }
+ }
+
+ /** Traversals keys *in sorted order* and applies given action. */
+ suspend fun forEachKeyAsync(action: suspend (key: K) -> Unit) {
+ for (index in array.indices step 2) {
+ action(array[index] as K)
+ }
+ }
+
+ /** Traversals key-value entries *in sorted order* and applies given action. */
+ fun forEach(action: (key: K, value: V) -> Unit) {
+ for (index in array.indices step 2) {
+ action(array[index] as K, array[index + 1] as V)
+ }
+ }
+
+ /** Traversals key-value entries in sorted order and applies given action. */
+ suspend fun forEachAsync(action: suspend (key: K, value: V) -> Unit) {
+ for (index in array.indices step 2) {
+ action(array[index] as K, array[index + 1] as V)
+ }
+ }
+
+ /**
+ * Returns the value associated with given key.
+ *
+ * Binary-search algorithm is applied, so this operation takes O(log2(N)) at worst case.
+ */
+ operator fun get(key: K): V? {
+ var low = 0
+ var high = array.size / 2
+ while (low < high) {
+ val mid = (low + high).ushr(1) // safe from overflows
+ val diff = (array[mid * 2] as K).compareTo(key)
+ when {
+ diff < 0 -> low = mid + 1
+ diff > 0 -> high = mid
+ else -> return array[mid * 2 + 1] as V
+ }
+ }
+ return null
+ }
+
+ /** Initializer to provide key-value pairs *in sorted order*. */
+ class OrderedInitializer<K : Comparable<K>, V>
+ internal constructor(private val array: Array<Any?>) {
+ private var index = 0
+
+ internal val size: Int
+ get() = array.size
+
+ /** Returns whether all elements are added. */
+ fun isDone() = index == array.size
+
+ /** Adds a new key-value entry. The key must be provided in sorted order. */
+ fun put(key: K, value: V) {
+ array[index++] = key
+ array[index++] = value
+ }
+
+ internal fun verify() {
+ if (!isDone()) throw IllegalStateException("Missing items: ${index / 2} / ${size / 2}")
+ for (index in 2 until size step 2) {
+ if ((array[index - 2] as K) >= (array[index] as K)) {
+ throw IllegalStateException("${array[index - 2]} >= ${array[index]}")
+ }
+ }
+ }
+ }
+}
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 9179f8ffc082..876f6152cccd 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -16,6 +16,8 @@
package com.android.settingslib.metadata
+import android.content.Context
+
/** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata) {
/**
@@ -32,7 +34,8 @@ open class PreferenceHierarchyNode internal constructor(val metadata: Preference
*
* A root hierarchy represents a preference screen. A sub-hierarchy represents a preference group.
*/
-class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
+class PreferenceHierarchy
+internal constructor(private val context: Context, metadata: PreferenceMetadata) :
PreferenceHierarchyNode(metadata) {
private val children = mutableListOf<PreferenceHierarchyNode>()
@@ -51,7 +54,8 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
*
* @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
*/
- operator fun String.unaryPlus() = +PreferenceHierarchyNode(PreferenceScreenRegistry[this]!!)
+ operator fun String.unaryPlus() =
+ +PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, this)!!)
operator fun PreferenceHierarchyNode.unaryPlus() = also { children.add(it) }
@@ -79,7 +83,7 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
/** Adds a preference group to the hierarchy before given key. */
fun addGroupBefore(key: String, metadata: PreferenceMetadata): PreferenceHierarchy {
val (list, index) = findPreference(key) ?: (children to children.size)
- return PreferenceHierarchy(metadata).also { list.add(index, it) }
+ return PreferenceHierarchy(context, metadata).also { list.add(index, it) }
}
/** Adds a preference to the hierarchy after given key. */
@@ -91,7 +95,7 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
/** Adds a preference group to the hierarchy after given key. */
fun addGroupAfter(key: String, metadata: PreferenceMetadata): PreferenceHierarchy {
val (list, index) = findPreference(key) ?: (children to children.size - 1)
- return PreferenceHierarchy(metadata).also { list.add(index + 1, it) }
+ return PreferenceHierarchy(context, metadata).also { list.add(index + 1, it) }
}
private fun findPreference(key: String): Pair<MutableList<PreferenceHierarchyNode>, Int>? {
@@ -106,12 +110,13 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
}
/** Adds a preference group to the hierarchy. */
- operator fun PreferenceGroup.unaryPlus() = PreferenceHierarchy(this).also { children.add(it) }
+ operator fun PreferenceGroup.unaryPlus() =
+ PreferenceHierarchy(context, this).also { children.add(it) }
/** Adds a preference group and returns its preference hierarchy. */
@JvmOverloads
fun addGroup(metadata: PreferenceGroup, order: Int? = null): PreferenceHierarchy =
- PreferenceHierarchy(metadata).also {
+ PreferenceHierarchy(context, metadata).also {
this.order = order
children.add(it)
}
@@ -128,7 +133,9 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
* @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
*/
fun addPreferenceScreen(screenKey: String) {
- children.add(PreferenceHierarchy(PreferenceScreenRegistry[screenKey]!!))
+ children.add(
+ PreferenceHierarchy(context, PreferenceScreenRegistry.create(context, screenKey)!!)
+ )
}
/** Extensions to add more preferences to the hierarchy. */
@@ -175,5 +182,8 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
* Builder function to create [PreferenceHierarchy] in
* [DSL](https://kotlinlang.org/docs/type-safe-builders.html) manner.
*/
-fun preferenceHierarchy(metadata: PreferenceMetadata, init: PreferenceHierarchy.() -> Unit) =
- PreferenceHierarchy(metadata).also(init)
+fun preferenceHierarchy(
+ context: Context,
+ metadata: PreferenceMetadata,
+ init: PreferenceHierarchy.() -> Unit,
+) = PreferenceHierarchy(context, metadata).also(init)
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 fc68ea7f8a2f..a2dcefb43436 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
@@ -64,3 +64,14 @@ interface PreferenceScreenMetadata : PreferenceMetadata {
*/
fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
}
+
+/** Creator of [PreferenceScreenMetadata]. */
+fun interface PreferenceScreenMetadataCreator {
+
+ /**
+ * Creates a new [PreferenceScreenMetadata].
+ *
+ * @param context application context to create the PreferenceScreenMetadata
+ */
+ fun create(context: Context): PreferenceScreenMetadata
+}
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 ff0991023393..6cf39f3ae8aa 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -18,11 +18,6 @@ package com.android.settingslib.metadata
import android.content.Context
import com.android.settingslib.datastore.KeyValueStore
-import com.google.common.base.Supplier
-import com.google.common.base.Suppliers
-import com.google.common.collect.ImmutableMap
-
-private typealias PreferenceScreenMap = ImmutableMap<String, PreferenceScreenMetadata>
/** Registry of all available preference screens in the app. */
object PreferenceScreenRegistry : ReadWritePermitProvider {
@@ -30,12 +25,12 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
/** Provider of key-value store. */
private lateinit var keyValueStoreProvider: KeyValueStoreProvider
- private var preferenceScreensSupplier: Supplier<PreferenceScreenMap> = Supplier {
- ImmutableMap.of()
- }
-
- val preferenceScreens: PreferenceScreenMap
- get() = preferenceScreensSupplier.get()
+ /**
+ * Creators of all available [PreferenceScreenMetadata]s.
+ *
+ * The map key is preference screen key.
+ */
+ var preferenceScreenMetadataCreators = FixedArrayMap<String, PreferenceScreenMetadataCreator>()
private var readWritePermitProvider: ReadWritePermitProvider =
object : ReadWritePermitProvider {}
@@ -54,26 +49,9 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore? =
keyValueStoreProvider.getKeyValueStore(context, preference)
- /** Sets supplier to provide available preference screens. */
- fun setPreferenceScreensSupplier(supplier: Supplier<List<PreferenceScreenMetadata>>) {
- preferenceScreensSupplier =
- Suppliers.memoize {
- val screensBuilder = ImmutableMap.builder<String, PreferenceScreenMetadata>()
- for (screen in supplier.get()) screensBuilder.put(screen.key, screen)
- screensBuilder.buildOrThrow()
- }
- }
-
- /** Sets available preference screens. */
- fun setPreferenceScreens(vararg screens: PreferenceScreenMetadata) {
- val screensBuilder = ImmutableMap.builder<String, PreferenceScreenMetadata>()
- for (screen in screens) screensBuilder.put(screen.key, screen)
- preferenceScreensSupplier = Suppliers.ofInstance(screensBuilder.buildOrThrow())
- }
-
- /** Returns [PreferenceScreenMetadata] of particular key. */
- operator fun get(key: String?): PreferenceScreenMetadata? =
- if (key != null) preferenceScreens[key] else null
+ /** Creates [PreferenceScreenMetadata] of particular screen key. */
+ fun create(context: Context, screenKey: String?): PreferenceScreenMetadata? =
+ screenKey?.let { preferenceScreenMetadataCreators[it]?.create(context.applicationContext) }
/**
* 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/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index 991d5b7791e9..e237a6a4cf14 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -83,7 +83,7 @@ open class PreferenceFragment :
@XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0
protected fun getPreferenceScreenCreator(context: Context): PreferenceScreenCreator? =
- (PreferenceScreenRegistry[getPreferenceScreenBindingKey(context)]
+ (PreferenceScreenRegistry.create(context, getPreferenceScreenBindingKey(context))
as? PreferenceScreenCreator)
?.run { if (isFlagEnabled(context)) this else null }
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 a9e20f284a61..91abd8b4c9e9 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -220,7 +220,7 @@ class PreferenceScreenBindingHelper(
/** Updates preference screen that has incomplete hierarchy. */
@JvmStatic
fun bind(preferenceScreen: PreferenceScreen) {
- PreferenceScreenRegistry[preferenceScreen.key]?.run {
+ PreferenceScreenRegistry.create(preferenceScreen.context, preferenceScreen.key)?.run {
if (!hasCompleteHierarchy()) {
val preferenceBindingFactory =
(this as? PreferenceScreenCreator)?.preferenceBindingFactory ?: return
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 7f99d7a9bbdd..211b3bdaea70 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
@@ -81,8 +81,8 @@ class PreferenceScreenFactory {
*
* The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
*/
- fun createBindingScreen(screenKey: String?): PreferenceScreen? {
- val metadata = PreferenceScreenRegistry[screenKey] ?: return null
+ fun createBindingScreen(context: Context, screenKey: String?): PreferenceScreen? {
+ val metadata = PreferenceScreenRegistry.create(context, screenKey) ?: return null
if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
return metadata.createPreferenceScreen(this)
}
@@ -93,11 +93,12 @@ class PreferenceScreenFactory {
/** Creates [PreferenceScreen] from [PreferenceScreenRegistry]. */
@JvmStatic
fun createBindingScreen(preference: Preference): PreferenceScreen? {
+ val context = preference.context
val preferenceScreenCreator =
- (PreferenceScreenRegistry[preference.key] as? PreferenceScreenCreator)
- ?: return null
+ (PreferenceScreenRegistry.create(context, preference.key)
+ as? PreferenceScreenCreator) ?: return null
if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
- val factory = PreferenceScreenFactory(preference.context)
+ val factory = PreferenceScreenFactory(context)
val preferenceScreen = preferenceScreenCreator.createPreferenceScreen(factory)
factory.preferenceManager.setPreferences(preferenceScreen)
return preferenceScreen