diff options
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 |