summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vadym Omelnytskyi <omelnytskyi@google.com> 2025-02-12 16:46:23 +0000
committer Vadym Omelnytskyi <omelnytskyi@google.com> 2025-03-04 16:13:32 -0800
commit14c82ceecd06e0d395c7368beb611f8ce1bdfbf7 (patch)
tree0e15137238540fb321d4caf184121d32cc395c89
parenta440a7d4a085436aceb7becee8b53e2bb6e1626a (diff)
Display: add System Settings Flow & Delegate
Added System Settings Flow that allows to observe and react on Integer System settings values using Kotlin Coroutines. Also add System Settings Integer delegate. Flag: EXEMPT only usage guarded by another flag Test: atest com.android.settingslib.spaprivileged.settingsprovider Test: atest --iteration 30 -c frameworks/base/packages/SettingsLib/SpaPrivileged/tests/robotests Test: verified that SettingsSystemIntegerFlow works with `display_color_mode` Int setting w/ and /wo set value. Bug: 390644464 Change-Id: Iacda050b9ff44bda4358756848b15319c699eef1
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt60
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp59
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml25
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties16
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt136
5 files changed, 296 insertions, 0 deletions
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt
new file mode 100644
index 000000000000..db7a640be893
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.spaprivileged.settingsprovider
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings
+import com.android.settingslib.spaprivileged.database.contentChangeFlow
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+fun Context.settingsSystemInteger(
+ name: String,
+ defaultValue: Int
+): ReadWriteProperty<Any?, Int> = SettingsSystemIntegerDelegate(this, name, defaultValue)
+
+fun Context.settingsSystemIntegerFlow(name: String, defaultValue: Int): Flow<Int> {
+ val value by settingsSystemInteger(name, defaultValue)
+ return contentChangeFlow(Settings.System.getUriFor(name))
+ .map { value }
+ .distinctUntilChanged()
+ .conflate()
+ .flowOn(Dispatchers.IO)
+}
+
+private class SettingsSystemIntegerDelegate(
+ context: Context,
+ private val name: String,
+ private val defaultValue: Int,
+) : ReadWriteProperty<Any?, Int> {
+
+ private val contentResolver: ContentResolver = context.contentResolver
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): Int =
+ Settings.System.getInt(contentResolver, name, defaultValue)
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
+ Settings.System.putInt(contentResolver, name, value)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp b/packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp
new file mode 100644
index 000000000000..e3faf73a69fb
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp
@@ -0,0 +1,59 @@
+//
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "SpaPrivilegedRoboTestStub",
+ defaults: [
+ "SpaPrivilegedLib-defaults",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ privileged: true,
+}
+
+android_robolectric_test {
+ name: "SpaPrivilegedRoboTests",
+ srcs: [
+ ":SpaPrivilegedLib_srcs",
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ defaults: [
+ "SpaPrivilegedLib-defaults",
+ ],
+
+ static_libs: [
+ "SpaLibTestUtils",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ ],
+
+ java_resource_dirs: [
+ "config",
+ ],
+
+ instrumentation_for: "SpaPrivilegedRoboTestStub",
+
+ test_options: {
+ timeout: 36000,
+ },
+
+ strict_mode: false,
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml
new file mode 100644
index 000000000000..113852d74f69
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ coreApp="true"
+ package="com.android.settingslib.spaprivileged.settingsprovider">
+
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+ <application/>
+</manifest> \ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties b/packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties
new file mode 100644
index 000000000000..95a24bde00f6
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties
@@ -0,0 +1,16 @@
+/*
+* 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.
+*/
+sdk=NEWEST_SDK \ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt
new file mode 100644
index 000000000000..67e4180a624b
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.spaprivileged.settingsprovider
+
+import android.content.Context
+import android.provider.Settings
+
+import androidx.test.core.app.ApplicationProvider
+
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.google.common.truth.Truth.assertThat
+
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class SettingsSystemIntegerTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Before
+ fun setUp() {
+ Settings.System.putString(context.contentResolver, TEST_NAME, null)
+ }
+
+ @Test
+ fun setIntValue_returnSameValueByDelegate() {
+ val settingValue = 250
+
+ Settings.System.putInt(context.contentResolver, TEST_NAME, settingValue)
+
+ val value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+ assertThat(value).isEqualTo(settingValue)
+ }
+
+ @Test
+ fun setZero_returnZeroByDelegate() {
+ val settingValue = 0
+ Settings.System.putInt(context.contentResolver, TEST_NAME, settingValue)
+
+ val value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+ assertThat(value).isEqualTo(settingValue)
+ }
+
+ @Test
+ fun setValueByDelegate_getValueFromSettings() {
+ val settingsValue = 5
+ var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+ value = settingsValue
+
+ assertThat(Settings.System.getInt(context.contentResolver, TEST_NAME, TEST_SETTING_DEFAULT_VALUE)).isEqualTo(settingsValue)
+ }
+
+ @Test
+ fun setZeroByDelegate_getZeroFromSettings() {
+ val settingValue = 0
+ var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+ value = settingValue
+
+ assertThat(Settings.System.getInt(context.contentResolver, TEST_NAME, TEST_SETTING_DEFAULT_VALUE)).isEqualTo(settingValue)
+ }
+
+ @Test
+ fun setValueByDelegate_returnValueFromsettingsSystemIntegerFlow() = runBlocking<Unit> {
+ val settingValue = 7
+ var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+ value = settingValue
+
+ val flow = context.settingsSystemIntegerFlow(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+ assertThat(flow.firstWithTimeoutOrNull()).isEqualTo(settingValue)
+ }
+
+ @Test
+ fun setValueByDelegateTwice_collectAfterValueChanged_onlyKeepLatest() = runBlocking<Unit> {
+ val firstSettingValue = 5
+ val secondSettingValue = 10
+
+ var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+ value = firstSettingValue
+
+ val flow = context.settingsSystemIntegerFlow(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+ value = secondSettingValue
+
+ assertThat(flow.firstWithTimeoutOrNull()).isEqualTo(value)
+ }
+
+ @Test
+ fun settingsSystemIntegerFlow_collectBeforeValueChanged_getBoth() = runBlocking<Unit> {
+ val firstSettingValue = 12
+ val secondSettingValue = 17
+ val delay_ms = 100L
+
+ var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+ value = firstSettingValue
+
+
+ val listDeferred = async {
+ context.settingsSystemIntegerFlow(TEST_NAME, TEST_SETTING_DEFAULT_VALUE).toListWithTimeout()
+ }
+
+ delay(delay_ms)
+ value = secondSettingValue
+
+ assertThat(listDeferred.await())
+ .containsAtLeast(firstSettingValue, secondSettingValue).inOrder()
+ }
+
+ private companion object {
+ const val TEST_NAME = "test_system_integer_delegate"
+ const val TEST_SETTING_DEFAULT_VALUE = -1
+ }
+}