Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a72d49ad authored by Jacky Wang's avatar Jacky Wang Committed by Android (Google) Code Review
Browse files

Merge "[Catalyst] Support metrics logging for user actions" into main

parents 78f6e04c 3f9b8f2c
Loading
Loading
Loading
Loading
+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
+3 −0
Original line number Original line Diff line number Diff line
@@ -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 {}


+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)
}
+26 −4
Original line number Original line Diff line number Diff line
@@ -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 {
@@ -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
+15 −11
Original line number Original line Diff line number Diff line
@@ -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
@@ -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)
@@ -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


@@ -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