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

Commit bfdcf16b authored by Jacky Wang's avatar Jacky Wang
Browse files

Add Settings Provider data stores

Bug: 325144964
Test: Unit test
Flag: NONE Sync library
Change-Id: I826e92920f61d09fe8b544cd2bc2e8cd2c5eb721
parent 907fb870
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ android_library {
        "androidx.annotation_annotation",
        "androidx.collection_collection-ktx",
        "androidx.core_core-ktx",
        "error_prone_annotations",
        "guava",
    ],
    kotlincflags: ["-Xjvm-default=all"],
+1 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.settingslib.datastore
import android.content.SharedPreferences

/** Interface of key-value store. */
interface KeyValueStore {
interface KeyValueStore : KeyedObservable<String> {

    /** Returns if the storage contains persistent value of given key. */
    fun contains(key: String): Boolean
+33 −15
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settingslib.datastore
import androidx.annotation.AnyThread
import androidx.annotation.GuardedBy
import androidx.collection.MutableScatterMap
import com.google.errorprone.annotations.CanIgnoreReturnValue
import java.util.WeakHashMap
import java.util.concurrent.Executor

@@ -62,8 +63,9 @@ interface KeyedObservable<K> {
     *
     * @param observer observer to be notified
     * @param executor executor to run the callback
     * @return if the observer is newly added
     */
    fun addObserver(observer: KeyedObserver<K?>, executor: Executor)
    @CanIgnoreReturnValue fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean

    /**
     * Adds an observer on given key.
@@ -73,14 +75,24 @@ interface KeyedObservable<K> {
     * @param key key to observe
     * @param observer observer to be notified
     * @param executor executor to run the callback
     * @return if the observer is newly added
     */
    fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor)
    @CanIgnoreReturnValue
    fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean

    /** Removes observer. */
    fun removeObserver(observer: KeyedObserver<K?>)
    /**
     * Removes observer.
     *
     * @return if the observer is found and removed
     */
    @CanIgnoreReturnValue fun removeObserver(observer: KeyedObserver<K?>): Boolean

    /** Removes observer on given key. */
    fun removeObserver(key: K, observer: KeyedObserver<K>)
    /**
     * Removes observer on given key.
     *
     * @return if the observer is found and removed
     */
    @CanIgnoreReturnValue fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean

    /**
     * Notifies all observers that a change occurs.
@@ -111,14 +123,17 @@ open class KeyedDataObservable<K> : KeyedObservable<K> {
    @GuardedBy("itself")
    private val keyedObservers = MutableScatterMap<K, WeakHashMap<KeyedObserver<K>, Executor>>()

    override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) {
    @CanIgnoreReturnValue
    override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean {
        val oldExecutor = synchronized(observers) { observers.put(observer, executor) }
        if (oldExecutor != null && oldExecutor != executor) {
            throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
        }
        return oldExecutor == null
    }

    override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) {
    @CanIgnoreReturnValue
    override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean {
        val oldExecutor =
            synchronized(keyedObservers) {
                keyedObservers.getOrPut(key) { WeakHashMap() }.put(observer, executor)
@@ -126,19 +141,22 @@ open class KeyedDataObservable<K> : KeyedObservable<K> {
        if (oldExecutor != null && oldExecutor != executor) {
            throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
        }
        return oldExecutor == null
    }

    override fun removeObserver(observer: KeyedObserver<K?>) {
        synchronized(observers) { observers.remove(observer) }
    }
    @CanIgnoreReturnValue
    override fun removeObserver(observer: KeyedObserver<K?>) =
        synchronized(observers) { observers.remove(observer) } != null

    override fun removeObserver(key: K, observer: KeyedObserver<K>) {
    @CanIgnoreReturnValue
    override fun removeObserver(key: K, observer: KeyedObserver<K>) =
        synchronized(keyedObservers) {
            val observers = keyedObservers[key]
            if (observers?.remove(observer) != null && observers.isEmpty()) {
            val observers = keyedObservers[key] ?: return false
            val removed = observers.remove(observer) != null
            if (removed && observers.isEmpty()) {
                keyedObservers.remove(key)
            }
        }
            removed
        }

    override fun notifyChange(reason: Int) {
+81 −0
Original line number Diff line number Diff line
/*
 * 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.datastore

import android.content.ContentResolver
import android.content.Context
import android.provider.Settings.Global
import android.provider.Settings.SettingNotFoundException

/**
 * [KeyValueStore] for [Global] settings.
 *
 * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
 */
class SettingsGlobalStore private constructor(contentResolver: ContentResolver) :
    SettingsStore(contentResolver) {

    override val tag: String
        get() = "SettingsGlobalStore"

    override fun contains(key: String): Boolean = Global.getString(contentResolver, key) != null

    override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
        try {
            when (valueType) {
                Boolean::class.javaObjectType -> Global.getInt(contentResolver, key) != 0
                Float::class.javaObjectType -> Global.getFloat(contentResolver, key)
                Int::class.javaObjectType -> Global.getInt(contentResolver, key)
                Long::class.javaObjectType -> Global.getLong(contentResolver, key)
                String::class.javaObjectType -> Global.getString(contentResolver, key)
                else -> throw UnsupportedOperationException("Get $key $valueType")
            }
                as T?
        } catch (e: SettingNotFoundException) {
            null
        }

    override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
        if (value == null) {
            Global.putString(contentResolver, key, null)
            return
        }
        when (valueType) {
            Boolean::class.javaObjectType ->
                Global.putInt(contentResolver, key, if (value == true) 1 else 0)
            Float::class.javaObjectType -> Global.putFloat(contentResolver, key, value as Float)
            Int::class.javaObjectType -> Global.putInt(contentResolver, key, value as Int)
            Long::class.javaObjectType -> Global.putLong(contentResolver, key, value as Long)
            String::class.javaObjectType -> Global.putString(contentResolver, key, value as String)
            else -> throw UnsupportedOperationException("Set $key $valueType")
        }
    }

    companion object {
        @Volatile private var instance: SettingsGlobalStore? = null

        @JvmStatic
        fun get(context: Context): SettingsGlobalStore =
            instance
                ?: synchronized(this) {
                    instance
                        ?: SettingsGlobalStore(context.applicationContext.contentResolver).also {
                            instance = it
                        }
                }
    }
}
+81 −0
Original line number Diff line number Diff line
/*
 * 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.datastore

import android.content.ContentResolver
import android.content.Context
import android.provider.Settings.Secure
import android.provider.Settings.SettingNotFoundException

/**
 * [KeyValueStore] for [Secure] settings.
 *
 * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
 */
class SettingsSecureStore private constructor(contentResolver: ContentResolver) :
    SettingsStore(contentResolver) {

    override val tag: String
        get() = "SettingsSecureStore"

    override fun contains(key: String): Boolean = Secure.getString(contentResolver, key) != null

    override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
        try {
            when (valueType) {
                Boolean::class.javaObjectType -> Secure.getInt(contentResolver, key) != 0
                Float::class.javaObjectType -> Secure.getFloat(contentResolver, key)
                Int::class.javaObjectType -> Secure.getInt(contentResolver, key)
                Long::class.javaObjectType -> Secure.getLong(contentResolver, key)
                String::class.javaObjectType -> Secure.getString(contentResolver, key)
                else -> throw UnsupportedOperationException("Get $key $valueType")
            }
                as T?
        } catch (e: SettingNotFoundException) {
            null
        }

    override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
        if (value == null) {
            Secure.putString(contentResolver, key, null)
            return
        }
        when (valueType) {
            Boolean::class.javaObjectType ->
                Secure.putInt(contentResolver, key, if (value == true) 1 else 0)
            Float::class.javaObjectType -> Secure.putFloat(contentResolver, key, value as Float)
            Int::class.javaObjectType -> Secure.putInt(contentResolver, key, value as Int)
            Long::class.javaObjectType -> Secure.putLong(contentResolver, key, value as Long)
            String::class.javaObjectType -> Secure.putString(contentResolver, key, value as String)
            else -> throw UnsupportedOperationException("Set $key $valueType")
        }
    }

    companion object {
        @Volatile private var instance: SettingsSecureStore? = null

        @JvmStatic
        fun get(context: Context): SettingsSecureStore =
            instance
                ?: synchronized(this) {
                    instance
                        ?: SettingsSecureStore(context.applicationContext.contentResolver).also {
                            instance = it
                        }
                }
    }
}
Loading