Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt +22 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) } } } packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/FlowsTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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" } } } packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt +8 −0 Original line number Diff line number Diff line Loading @@ -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 } packages/SettingsLib/SpaPrivileged/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -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> packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt 0 → 100644 +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
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt +22 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) } } }
packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/FlowsTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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" } } }
packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt +8 −0 Original line number Diff line number Diff line Loading @@ -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 }
packages/SettingsLib/SpaPrivileged/AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -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>
packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt 0 → 100644 +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) } }