diff options
8 files changed, 182 insertions, 51 deletions
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt index 754d5dc0c9c6..2a87452b0b6a 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt @@ -27,7 +27,11 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext -/** Defines interface for classes that can provide access to data from [Settings.Secure]. */ +/** + * Defines interface for classes that can provide access to data from [Settings.Secure]. + * This repository doesn't guarantee to provide value across different users. For that + * see: [UserAwareSecureSettingsRepository] + */ interface SecureSettingsRepository { /** Returns a [Flow] tracking the value of a setting as an [Int]. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt index ec29bd6014ef..89cdd25181cb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt @@ -32,19 +32,13 @@ import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT -import com.android.systemui.user.data.repository.UserRepository -import com.android.systemui.util.settings.SecureSettings -import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.onStart import javax.inject.Inject interface StickyKeysRepository { @@ -53,14 +47,12 @@ interface StickyKeysRepository { } @SysUISingleton -@OptIn(ExperimentalCoroutinesApi::class) class StickyKeysRepositoryImpl @Inject constructor( private val inputManager: InputManager, @Background private val backgroundDispatcher: CoroutineDispatcher, - private val secureSettings: SecureSettings, - userRepository: UserRepository, + secureSettingsRepository: UserAwareSecureSettingsRepository, private val stickyKeysLogger: StickyKeysLogger, ) : StickyKeysRepository { @@ -78,25 +70,10 @@ constructor( .flowOn(backgroundDispatcher) override val settingEnabled: Flow<Boolean> = - userRepository.selectedUserInfo - .flatMapLatest { stickyKeySettingObserver(it.id) } - .flowOn(backgroundDispatcher) - - private fun stickyKeySettingObserver(userId: Int): Flow<Boolean> { - return secureSettings - .observerFlow(userId, SETTING_KEY) - .onStart { emit(Unit) } - .map { isSettingEnabledForCurrentUser(userId) } - .distinctUntilChanged() + secureSettingsRepository + .boolSettingForActiveUser(SETTING_KEY, defaultValue = false) .onEach { stickyKeysLogger.logNewSettingValue(it) } - } - - private fun isSettingEnabledForCurrentUser(userId: Int) = - secureSettings.getIntForUser( - /* name= */ SETTING_KEY, - /* default= */ 0, - /* userHandle= */ userId - ) != 0 + .flowOn(backgroundDispatcher) private fun toStickyKeysMap(state: StickyModifierState): LinkedHashMap<ModifierKey, Locked> { val keys = linkedMapOf<ModifierKey, Locked>() diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java index f36c335e0f44..d509b2da482e 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java @@ -16,6 +16,9 @@ package com.android.systemui.util.settings; +import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository; +import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl; + import dagger.Binds; import dagger.Module; @@ -36,4 +39,9 @@ public interface SettingsUtilModule { /** Bind GlobalSettingsImpl to GlobalSettings. */ @Binds GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl); + + /** Bind UserAwareSecureSettingsRepositoryImpl to UserAwareSecureSettingsRepository. */ + @Binds + UserAwareSecureSettingsRepository bindsUserAwareSecureSettingsRepository( + UserAwareSecureSettingsRepositoryImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt new file mode 100644 index 000000000000..d3e50803b5d5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepository.kt @@ -0,0 +1,70 @@ +/* + * 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.systemui.util.settings.repository + +import android.provider.Settings +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxy +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import javax.inject.Inject + +/** + * Repository for observing values of [Settings.Secure] for the currently active user. That means + * when user is switched and the new user has different value, flow will emit new value. + */ +interface UserAwareSecureSettingsRepository { + + /** + * Emits boolean value of the setting for active user. Also emits starting value when + * subscribed. + * See: [SettingsProxy.getBool]. + */ + fun boolSettingForActiveUser(name: String, defaultValue: Boolean = false): Flow<Boolean> +} + +@SysUISingleton +@OptIn(ExperimentalCoroutinesApi::class) +class UserAwareSecureSettingsRepositoryImpl @Inject constructor( + private val secureSettings: SecureSettings, + private val userRepository: UserRepository, + @Background private val backgroundDispatcher: CoroutineDispatcher, +) : UserAwareSecureSettingsRepository { + + override fun boolSettingForActiveUser(name: String, defaultValue: Boolean): Flow<Boolean> = + userRepository.selectedUserInfo + .flatMapLatest { userInfo -> settingObserver(name, defaultValue, userInfo.id) } + .distinctUntilChanged() + .flowOn(backgroundDispatcher) + + private fun settingObserver(name: String, defaultValue: Boolean, userId: Int): Flow<Boolean> { + return secureSettings + .observerFlow(userId, name) + .onStart { emit(Unit) } + .map { secureSettings.getBoolForUser(name, defaultValue, userId) } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt index 6eebb6d19e5e..d14d72d90a31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher @@ -68,11 +69,15 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { @Before fun setup() { + val settingsRepository = UserAwareSecureSettingsRepositoryImpl( + secureSettings, + userRepository, + dispatcher + ) val stickyKeysRepository = StickyKeysRepositoryImpl( inputManager, dispatcher, - secureSettings, - userRepository, + settingsRepository, mock<StickyKeysLogger>() ) setStickyKeySetting(enabled = false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt index ed80a869dda8..913759f77013 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt @@ -1,16 +1,28 @@ -package com.android.systemui.keyboard.stickykeys.data.repository +/* + * 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.systemui.util.settings.repository import android.content.pm.UserInfo -import android.hardware.input.InputManager -import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues -import com.android.systemui.keyboard.stickykeys.StickyKeysLogger import com.android.systemui.kosmos.Kosmos import com.android.systemui.user.data.repository.fakeUserRepository -import com.android.systemui.util.mockito.mock import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -26,26 +38,24 @@ import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) -class StickyKeysRepositoryImplTest : SysuiTestCase() { +class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() { private val dispatcher = StandardTestDispatcher() private val testScope = TestScope(dispatcher) private val secureSettings = FakeSettings() private val userRepository = Kosmos().fakeUserRepository - private lateinit var stickyKeysRepository: StickyKeysRepositoryImpl + private lateinit var repository: UserAwareSecureSettingsRepository @Before fun setup() { - stickyKeysRepository = StickyKeysRepositoryImpl( - mock<InputManager>(), - dispatcher, + repository = UserAwareSecureSettingsRepositoryImpl( secureSettings, userRepository, - mock<StickyKeysLogger>() + dispatcher, ) userRepository.setUserInfos(USER_INFOS) - setStickyKeySettingForUser(enabled = true, userInfo = SETTING_ENABLED_USER) - setStickyKeySettingForUser(enabled = false, userInfo = SETTING_DISABLED_USER) + setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER) + setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER) } @Test @@ -53,7 +63,7 @@ class StickyKeysRepositoryImplTest : SysuiTestCase() { testScope.runTest { userRepository.setSelectedUserInfo(SETTING_ENABLED_USER) - val enabled by collectLastValue(stickyKeysRepository.settingEnabled) + val enabled by collectLastValue(repository.boolSettingForActiveUser(SETTING_NAME)) assertThat(enabled).isTrue() } @@ -63,10 +73,10 @@ class StickyKeysRepositoryImplTest : SysuiTestCase() { fun settingEnabledEmitsNewValueWhenSettingChanges() { testScope.runTest { userRepository.setSelectedUserInfo(SETTING_ENABLED_USER) - val enabled by collectValues(stickyKeysRepository.settingEnabled) + val enabled by collectValues(repository.boolSettingForActiveUser(SETTING_NAME)) runCurrent() - setStickyKeySettingForUser(enabled = false, userInfo = SETTING_ENABLED_USER) + setSettingValueForUser(enabled = false, userInfo = SETTING_ENABLED_USER) assertThat(enabled).containsExactly(true, false).inOrder() } @@ -76,7 +86,7 @@ class StickyKeysRepositoryImplTest : SysuiTestCase() { fun settingEnabledEmitsValueForNewUserWhenUserChanges() { testScope.runTest { userRepository.setSelectedUserInfo(SETTING_ENABLED_USER) - val enabled by collectLastValue(stickyKeysRepository.settingEnabled) + val enabled by collectLastValue(repository.boolSettingForActiveUser(SETTING_NAME)) runCurrent() userRepository.setSelectedUserInfo(SETTING_DISABLED_USER) @@ -85,12 +95,12 @@ class StickyKeysRepositoryImplTest : SysuiTestCase() { } } - private fun setStickyKeySettingForUser(enabled: Boolean, userInfo: UserInfo) { - val newValue = if (enabled) "1" else "0" - secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, userInfo.id) + private fun setSettingValueForUser(enabled: Boolean, userInfo: UserInfo) { + secureSettings.putBoolForUser(SETTING_NAME, enabled, userInfo.id) } private companion object { + const val SETTING_NAME = "SETTING_NAME" val SETTING_ENABLED_USER = UserInfo(/* id= */ 0, "user1", /* flags= */ 0) val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0) val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt new file mode 100644 index 000000000000..5054e29534e9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeUserAwareSecureSettingsRepository.kt @@ -0,0 +1,35 @@ +/* + * 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.systemui.util.settings + +import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map + +class FakeUserAwareSecureSettingsRepository : UserAwareSecureSettingsRepository { + + private val settings = MutableStateFlow<Map<String, Boolean>>(mutableMapOf()) + + override fun boolSettingForActiveUser(name: String, defaultValue: Boolean): Flow<Boolean> { + return settings.map { it.getOrDefault(name, defaultValue) } + } + + fun setBoolSettingForActiveUser(name: String, value: Boolean) { + settings.value = settings.value.toMutableMap().apply { this[name] = value } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt new file mode 100644 index 000000000000..94b2bdf63608 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/UserAwareSecureSettingsRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * 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.systemui.util.settings + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.userAwareSecureSettingsRepository by + Kosmos.Fixture { FakeUserAwareSecureSettingsRepository() } |