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

Commit 089e677c authored by Chaohui Wang's avatar Chaohui Wang
Browse files

New flow for Settings.Global

To easily collect the setting changes.

Bug: 308903704
Test: unit tests
Change-Id: I0b1ad312f137710ceda009adc782e5c217db787e
parent 4ce39bf5
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -16,10 +16,16 @@

package com.android.settingslib.spa.framework.util

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch

/**
 * Returns a [Flow] whose values are a list which containing the results of applying the given
@@ -46,3 +52,19 @@ inline fun <T> Flow<List<T>>.filterItem(crossinline predicate: (T) -> Boolean):
 */
fun <T1, T2> Flow<T1>.waitFirst(otherFlow: Flow<T2>): Flow<T1> =
    combine(otherFlow.take(1)) { value, _ -> value }


/**
 * Collects the latest value of given flow with a provided action with [LifecycleOwner].
 */
fun <T> Flow<T>.collectLatestWithLifecycle(
    lifecycleOwner: LifecycleOwner,
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
    action: suspend (value: T) -> Unit,
) {
    lifecycleOwner.lifecycleScope.launch {
        lifecycleOwner.repeatOnLifecycle(minActiveState) {
            collectLatest(action)
        }
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.settingslib.spa.framework.util

import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.waitUntil
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.count
import kotlinx.coroutines.flow.emptyFlow
@@ -85,4 +87,16 @@ class FlowsTest {

        assertThat(outputFlow.toList()).containsExactly("A")
    }

    @Test
    fun collectLatestWithLifecycle() {
        val mainFlow = flowOf("A")
        var actual: String? = null

        mainFlow.collectLatestWithLifecycle(TestLifecycleOwner()) {
            actual = it
        }

        waitUntil { actual == "A" }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -18,9 +18,17 @@ package com.android.settingslib.spa.testutils

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.withTimeoutOrNull

suspend fun <T> Flow<T>.firstWithTimeoutOrNull(timeMillis: Long = 500): T? =
    withTimeoutOrNull(timeMillis) {
        first()
    }

suspend fun <T> Flow<T>.toListWithTimeout(timeMillis: Long = 500): List<T> {
    val list = mutableListOf<T>()
    return withTimeoutOrNull(timeMillis) {
        toList(list)
    } ?: list
}
+1 −1
Original line number Diff line number Diff line
@@ -18,5 +18,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.settingslib.spaprivileged">
    <uses-permission android:name="android.permission.MANAGE_USERS" />
    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
</manifest>
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.spaprivileged.settingsprovider

import android.content.ContentResolver
import android.content.Context
import android.provider.Settings
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import kotlinx.coroutines.flow.Flow

fun Context.settingsGlobalBoolean(name: String): ReadWriteProperty<Any?, Boolean> =
    SettingsGlobalBooleanDelegate(this, name)

fun Context.settingsGlobalBooleanFlow(name: String): Flow<Boolean> {
    val value by settingsGlobalBoolean(name)
    return settingsGlobalFlow(name) { value }
}

private class SettingsGlobalBooleanDelegate(context: Context, private val name: String) :
    ReadWriteProperty<Any?, Boolean> {

    private val contentResolver: ContentResolver = context.contentResolver

    override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean =
        Settings.Global.getInt(contentResolver, name, 0) != 0

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
        Settings.Global.putInt(contentResolver, name, if (value) 1 else 0)
    }
}
Loading