Loading packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt 0 → 100644 +60 −0 Original line number 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.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) } } packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp 0 → 100644 +59 −0 Original line number 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 { 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, } packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml 0 → 100644 +25 −0 Original line number Diff line number Diff line <?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 packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties 0 → 100644 +16 −0 Original line number 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. */ sdk=NEWEST_SDK No newline at end of file packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt 0 → 100644 +136 −0 Original line number 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.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 } } Loading
packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt 0 → 100644 +60 −0 Original line number 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.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) } }
packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp 0 → 100644 +59 −0 Original line number 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 { 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, }
packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml 0 → 100644 +25 −0 Original line number Diff line number Diff line <?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
packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties 0 → 100644 +16 −0 Original line number 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. */ sdk=NEWEST_SDK No newline at end of file
packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt 0 → 100644 +136 −0 Original line number 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.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 } }