Loading packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt 0 → 100644 +35 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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 /** Metrics logger for preference actions triggered by user interaction. */ interface PreferenceUiActionMetricsLogger { /** * Logs preference value change due to user interaction. * * Note: Preference value changed by external Set is excluded. */ fun logPreferenceValueChange( screen: PreferenceScreenMetadata, preference: PreferenceMetadata, value: Any?, ) {} } /** Metrics logger for preference remote operations (e.g. external get/set). */ interface PreferenceRemoteOpMetricsLogger packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,9 @@ object PreferenceScreenRegistry : ReadWritePermitProvider { */ */ var preferenceScreenMetadataFactories = FixedArrayMap<String, PreferenceScreenMetadataFactory>() var preferenceScreenMetadataFactories = FixedArrayMap<String, PreferenceScreenMetadataFactory>() /** Metrics logger for preference actions triggered by user interaction. */ var preferenceUiActionMetricsLogger: PreferenceUiActionMetricsLogger? = null private var readWritePermitProvider: ReadWritePermitProvider = private var readWritePermitProvider: ReadWritePermitProvider = object : ReadWritePermitProvider {} object : ReadWritePermitProvider {} Loading packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreDelegate.kt 0 → 100644 +52 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.preference import androidx.preference.PreferenceDataStore /** [PreferenceDataStore] delegate. */ open class PreferenceDataStoreDelegate(internal val delegate: PreferenceDataStore) : PreferenceDataStore() { override fun getBoolean(key: String, defValue: Boolean): Boolean = delegate.getBoolean(key, defValue) override fun getFloat(key: String, defValue: Float): Float = delegate.getFloat(key, defValue) override fun getInt(key: String, defValue: Int): Int = delegate.getInt(key, defValue) override fun getLong(key: String, defValue: Long): Long = delegate.getLong(key, defValue) override fun getString(key: String, defValue: String?): String? = delegate.getString(key, defValue) override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? = delegate.getStringSet(key, defValues) override fun putBoolean(key: String, value: Boolean) = delegate.putBoolean(key, value) override fun putFloat(key: String, value: Float) = delegate.putFloat(key, value) override fun putInt(key: String, value: Int) = delegate.putInt(key, value) override fun putLong(key: String, value: Long) = delegate.putLong(key, value) override fun putString(key: String, value: String?) = delegate.putString(key, value) override fun putStringSet(key: String, values: Set<String>?) = delegate.putStringSet(key, values) } packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt +26 −4 Original line number Original line Diff line number Diff line Loading @@ -18,15 +18,30 @@ package com.android.settingslib.preference import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceGroup import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceHierarchy import com.android.settingslib.metadata.PreferenceHierarchy import com.android.settingslib.metadata.PreferenceScreenMetadata /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */ /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */ fun PreferenceGroup.inflatePreferenceHierarchy( fun PreferenceScreen.inflatePreferenceHierarchy( preferenceBindingFactory: PreferenceBindingFactory, preferenceBindingFactory: PreferenceBindingFactory, hierarchy: PreferenceHierarchy, hierarchy: PreferenceHierarchy, storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(), ) = inflatePreferenceHierarchy( hierarchy.metadata as PreferenceScreenMetadata, preferenceBindingFactory, hierarchy, mutableMapOf(), ) /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */ private fun PreferenceGroup.inflatePreferenceHierarchy( preferenceScreenMetadata: PreferenceScreenMetadata, preferenceBindingFactory: PreferenceBindingFactory, hierarchy: PreferenceHierarchy, storages: MutableMap<KeyValueStore, PreferenceDataStore>, ) { ) { preferenceBindingFactory.bind(this, hierarchy) preferenceBindingFactory.bind(this, hierarchy) hierarchy.forEach { hierarchy.forEach { Loading @@ -38,11 +53,18 @@ fun PreferenceGroup.inflatePreferenceHierarchy( val preferenceGroup = widget as PreferenceGroup val preferenceGroup = widget as PreferenceGroup // MUST add preference before binding, otherwise exception is raised when add child // MUST add preference before binding, otherwise exception is raised when add child addPreference(preferenceGroup) addPreference(preferenceGroup) preferenceGroup.inflatePreferenceHierarchy(preferenceBindingFactory, it) preferenceGroup.inflatePreferenceHierarchy( preferenceScreenMetadata, preferenceBindingFactory, it, storages, ) } else { } else { (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> widget.preferenceDataStore = widget.preferenceDataStore = storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } storages.getOrPut(storage) { storage.toPreferenceDataStore(preferenceScreenMetadata, metadata) } } } preferenceBindingFactory.bind(widget, it, preferenceBinding) preferenceBindingFactory.bind(widget, it, preferenceBinding) // MUST add preference after binding for persistent preference to get initial value // MUST add preference after binding for persistent preference to get initial value Loading packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +15 −11 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.settingslib.metadata.PreferenceHierarchyNode import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceScreenMetadata import com.android.settingslib.metadata.PreferenceScreenRegistry import com.android.settingslib.metadata.PreferenceScreenRegistry import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMultimap import com.google.common.collect.ImmutableMultimap Loading Loading @@ -70,9 +71,7 @@ class PreferenceScreenBindingHelper( override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!! override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!! override fun getKeyValueStore(key: String) = override fun getKeyValueStore(key: String) = (findPreference<Preference>(key)?.preferenceDataStore findPreference<Preference>(key)?.preferenceDataStore?.findKeyValueStore() as? PreferenceDataStoreAdapter) ?.keyValueStore override fun notifyPreferenceChange(key: String) = override fun notifyPreferenceChange(key: String) = notifyChange(key, PreferenceChangeReason.STATE) notifyChange(key, PreferenceChangeReason.STATE) Loading Loading @@ -137,16 +136,21 @@ class PreferenceScreenBindingHelper( addObserver(preferenceObserver, mainExecutor) addObserver(preferenceObserver, mainExecutor) preferenceScreen.forEachRecursively { preferenceScreen.forEachRecursively { val preferenceDataStore = it.preferenceDataStore it.preferenceDataStore?.findKeyValueStore()?.let { keyValueStore -> if (preferenceDataStore is PreferenceDataStoreAdapter) { val key = it.key val key = it.key val keyValueStore = preferenceDataStore.keyValueStore storages[key] = keyValueStore storages[key] = keyValueStore keyValueStore.addObserver(key, storageObserver, mainExecutor) keyValueStore.addObserver(key, storageObserver, mainExecutor) } } } } } } private fun PreferenceDataStore.findKeyValueStore(): KeyValueStore? = when (this) { is PreferenceDataStoreAdapter -> keyValueStore is PreferenceDataStoreDelegate -> delegate.findKeyValueStore() else -> null } private fun onPreferenceChange(key: String?, reason: Int) { private fun onPreferenceChange(key: String?, reason: Int) { if (key == null) return if (key == null) return Loading Loading @@ -239,17 +243,17 @@ class PreferenceScreenBindingHelper( preferenceBindingFactory: PreferenceBindingFactory, preferenceBindingFactory: PreferenceBindingFactory, preferenceHierarchy: PreferenceHierarchy, preferenceHierarchy: PreferenceHierarchy, ) { ) { val preferenceScreenMetadata = preferenceHierarchy.metadata as PreferenceScreenMetadata val preferences = mutableMapOf<String, PreferenceHierarchyNode>() val preferences = mutableMapOf<String, PreferenceHierarchyNode>() preferenceHierarchy.forEachRecursively { preferenceHierarchy.forEachRecursively { preferences[it.metadata.key] = it } val metadata = it.metadata preferences[metadata.key] = it } val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>() val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>() fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) { fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) { (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> preferenceDataStore = preferenceDataStore = storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } storages.getOrPut(storage) { storage.toPreferenceDataStore(preferenceScreenMetadata, metadata) } } } } } Loading Loading
packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt 0 → 100644 +35 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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 /** Metrics logger for preference actions triggered by user interaction. */ interface PreferenceUiActionMetricsLogger { /** * Logs preference value change due to user interaction. * * Note: Preference value changed by external Set is excluded. */ fun logPreferenceValueChange( screen: PreferenceScreenMetadata, preference: PreferenceMetadata, value: Any?, ) {} } /** Metrics logger for preference remote operations (e.g. external get/set). */ interface PreferenceRemoteOpMetricsLogger
packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,9 @@ object PreferenceScreenRegistry : ReadWritePermitProvider { */ */ var preferenceScreenMetadataFactories = FixedArrayMap<String, PreferenceScreenMetadataFactory>() var preferenceScreenMetadataFactories = FixedArrayMap<String, PreferenceScreenMetadataFactory>() /** Metrics logger for preference actions triggered by user interaction. */ var preferenceUiActionMetricsLogger: PreferenceUiActionMetricsLogger? = null private var readWritePermitProvider: ReadWritePermitProvider = private var readWritePermitProvider: ReadWritePermitProvider = object : ReadWritePermitProvider {} object : ReadWritePermitProvider {} Loading
packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreDelegate.kt 0 → 100644 +52 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.preference import androidx.preference.PreferenceDataStore /** [PreferenceDataStore] delegate. */ open class PreferenceDataStoreDelegate(internal val delegate: PreferenceDataStore) : PreferenceDataStore() { override fun getBoolean(key: String, defValue: Boolean): Boolean = delegate.getBoolean(key, defValue) override fun getFloat(key: String, defValue: Float): Float = delegate.getFloat(key, defValue) override fun getInt(key: String, defValue: Int): Int = delegate.getInt(key, defValue) override fun getLong(key: String, defValue: Long): Long = delegate.getLong(key, defValue) override fun getString(key: String, defValue: String?): String? = delegate.getString(key, defValue) override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? = delegate.getStringSet(key, defValues) override fun putBoolean(key: String, value: Boolean) = delegate.putBoolean(key, value) override fun putFloat(key: String, value: Float) = delegate.putFloat(key, value) override fun putInt(key: String, value: Int) = delegate.putInt(key, value) override fun putLong(key: String, value: Long) = delegate.putLong(key, value) override fun putString(key: String, value: String?) = delegate.putString(key, value) override fun putStringSet(key: String, values: Set<String>?) = delegate.putStringSet(key, values) }
packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceHierarchyInflater.kt +26 −4 Original line number Original line Diff line number Diff line Loading @@ -18,15 +18,30 @@ package com.android.settingslib.preference import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceGroup import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceHierarchy import com.android.settingslib.metadata.PreferenceHierarchy import com.android.settingslib.metadata.PreferenceScreenMetadata /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */ /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */ fun PreferenceGroup.inflatePreferenceHierarchy( fun PreferenceScreen.inflatePreferenceHierarchy( preferenceBindingFactory: PreferenceBindingFactory, preferenceBindingFactory: PreferenceBindingFactory, hierarchy: PreferenceHierarchy, hierarchy: PreferenceHierarchy, storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(), ) = inflatePreferenceHierarchy( hierarchy.metadata as PreferenceScreenMetadata, preferenceBindingFactory, hierarchy, mutableMapOf(), ) /** Inflates [PreferenceHierarchy] into given [PreferenceGroup] recursively. */ private fun PreferenceGroup.inflatePreferenceHierarchy( preferenceScreenMetadata: PreferenceScreenMetadata, preferenceBindingFactory: PreferenceBindingFactory, hierarchy: PreferenceHierarchy, storages: MutableMap<KeyValueStore, PreferenceDataStore>, ) { ) { preferenceBindingFactory.bind(this, hierarchy) preferenceBindingFactory.bind(this, hierarchy) hierarchy.forEach { hierarchy.forEach { Loading @@ -38,11 +53,18 @@ fun PreferenceGroup.inflatePreferenceHierarchy( val preferenceGroup = widget as PreferenceGroup val preferenceGroup = widget as PreferenceGroup // MUST add preference before binding, otherwise exception is raised when add child // MUST add preference before binding, otherwise exception is raised when add child addPreference(preferenceGroup) addPreference(preferenceGroup) preferenceGroup.inflatePreferenceHierarchy(preferenceBindingFactory, it) preferenceGroup.inflatePreferenceHierarchy( preferenceScreenMetadata, preferenceBindingFactory, it, storages, ) } else { } else { (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> widget.preferenceDataStore = widget.preferenceDataStore = storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } storages.getOrPut(storage) { storage.toPreferenceDataStore(preferenceScreenMetadata, metadata) } } } preferenceBindingFactory.bind(widget, it, preferenceBinding) preferenceBindingFactory.bind(widget, it, preferenceBinding) // MUST add preference after binding for persistent preference to get initial value // MUST add preference after binding for persistent preference to get initial value Loading
packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +15 −11 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,7 @@ import com.android.settingslib.metadata.PreferenceHierarchyNode import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceScreenMetadata import com.android.settingslib.metadata.PreferenceScreenRegistry import com.android.settingslib.metadata.PreferenceScreenRegistry import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMultimap import com.google.common.collect.ImmutableMultimap Loading Loading @@ -70,9 +71,7 @@ class PreferenceScreenBindingHelper( override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!! override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!! override fun getKeyValueStore(key: String) = override fun getKeyValueStore(key: String) = (findPreference<Preference>(key)?.preferenceDataStore findPreference<Preference>(key)?.preferenceDataStore?.findKeyValueStore() as? PreferenceDataStoreAdapter) ?.keyValueStore override fun notifyPreferenceChange(key: String) = override fun notifyPreferenceChange(key: String) = notifyChange(key, PreferenceChangeReason.STATE) notifyChange(key, PreferenceChangeReason.STATE) Loading Loading @@ -137,16 +136,21 @@ class PreferenceScreenBindingHelper( addObserver(preferenceObserver, mainExecutor) addObserver(preferenceObserver, mainExecutor) preferenceScreen.forEachRecursively { preferenceScreen.forEachRecursively { val preferenceDataStore = it.preferenceDataStore it.preferenceDataStore?.findKeyValueStore()?.let { keyValueStore -> if (preferenceDataStore is PreferenceDataStoreAdapter) { val key = it.key val key = it.key val keyValueStore = preferenceDataStore.keyValueStore storages[key] = keyValueStore storages[key] = keyValueStore keyValueStore.addObserver(key, storageObserver, mainExecutor) keyValueStore.addObserver(key, storageObserver, mainExecutor) } } } } } } private fun PreferenceDataStore.findKeyValueStore(): KeyValueStore? = when (this) { is PreferenceDataStoreAdapter -> keyValueStore is PreferenceDataStoreDelegate -> delegate.findKeyValueStore() else -> null } private fun onPreferenceChange(key: String?, reason: Int) { private fun onPreferenceChange(key: String?, reason: Int) { if (key == null) return if (key == null) return Loading Loading @@ -239,17 +243,17 @@ class PreferenceScreenBindingHelper( preferenceBindingFactory: PreferenceBindingFactory, preferenceBindingFactory: PreferenceBindingFactory, preferenceHierarchy: PreferenceHierarchy, preferenceHierarchy: PreferenceHierarchy, ) { ) { val preferenceScreenMetadata = preferenceHierarchy.metadata as PreferenceScreenMetadata val preferences = mutableMapOf<String, PreferenceHierarchyNode>() val preferences = mutableMapOf<String, PreferenceHierarchyNode>() preferenceHierarchy.forEachRecursively { preferenceHierarchy.forEachRecursively { preferences[it.metadata.key] = it } val metadata = it.metadata preferences[metadata.key] = it } val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>() val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>() fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) { fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) { (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> preferenceDataStore = preferenceDataStore = storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } storages.getOrPut(storage) { storage.toPreferenceDataStore(preferenceScreenMetadata, metadata) } } } } } Loading