diff options
| author | 2023-07-11 17:03:33 +0000 | |
|---|---|---|
| committer | 2023-07-11 17:03:33 +0000 | |
| commit | f56b0db7ce804dff686ec2bc715858ceea7c9f60 (patch) | |
| tree | 5d707988fe50782d53309a1f4378207fbd019b73 | |
| parent | eaf79c2013ccaa1899d7429815a326e2435059b3 (diff) | |
| parent | 778b918db21efd1a7c3358c7e54cff71e1cba8c5 (diff) | |
Merge "[flexiglass] Centralizes automatic scene changing business logic." into udc-qpr-dev
13 files changed, 328 insertions, 188 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 2513c81c17f8..62a484d42dec 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -53,6 +52,7 @@ constructor( private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, + featureFlags: FeatureFlags, @Assisted private val containerName: String, ) { @@ -95,36 +95,14 @@ constructor( val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { - // UNLOCKING SHOWS Gone. - // - // Move to the gone scene if the device becomes unlocked while on the bouncer scene. - applicationScope.launch { - sceneInteractor - .currentScene(containerName) - .flatMapLatest { currentScene -> - if (currentScene.key == SceneKey.Bouncer) { - authenticationInteractor.isUnlocked - } else { - flowOf(false) - } - } - .distinctUntilChanged() - .collect { isUnlocked -> - if (isUnlocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + // Clear the message if moved from throttling to no-longer throttling. + applicationScope.launch { + isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> + if (wasThrottled && !currentlyThrottled) { + clearMessage() } } - } - - // Clear the message if moved from throttling to no-longer throttling. - applicationScope.launch { - isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> - if (wasThrottled && !currentlyThrottled) { - clearMessage() - } } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 5425022e11b7..a4ef5cec6525 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -23,6 +23,8 @@ import com.android.systemui.R import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -49,6 +51,7 @@ constructor( @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, interactorFactory: BouncerInteractor.Factory, + featureFlags: FeatureFlags, @Assisted containerName: String, ) { private val interactor: BouncerInteractor = interactorFactory.create(containerName) @@ -102,15 +105,48 @@ constructor( ) init { - applicationScope.launch { - _authMethod.subscriptionCount - .pairwise() - .map { (previousCount, currentCount) -> currentCount > previousCount } - .collect { subscriberAdded -> - if (subscriberAdded) { - reloadAuthMethod() + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + applicationScope.launch { + interactor.isThrottled + .map { isThrottled -> + if (isThrottled) { + when (interactor.getAuthenticationMethod()) { + is AuthenticationMethodModel.Pin -> + R.string.kg_too_many_failed_pin_attempts_dialog_message + is AuthenticationMethodModel.Password -> + R.string.kg_too_many_failed_password_attempts_dialog_message + is AuthenticationMethodModel.Pattern -> + R.string.kg_too_many_failed_pattern_attempts_dialog_message + else -> null + }?.let { stringResourceId -> + applicationContext.getString( + stringResourceId, + interactor.throttling.value.failedAttemptCount, + ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), + ) + } + } else { + null + } + } + .distinctUntilChanged() + .collect { dialogMessageOrNull -> + if (dialogMessageOrNull != null) { + _throttlingDialogMessage.value = dialogMessageOrNull + } + } + } + + applicationScope.launch { + _authMethod.subscriptionCount + .pairwise() + .map { (previousCount, currentCount) -> currentCount > previousCount } + .collect { subscriberAdded -> + if (subscriberAdded) { + reloadAuthMethod() + } } - } + } } } @@ -144,39 +180,6 @@ constructor( */ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() - init { - applicationScope.launch { - interactor.isThrottled - .map { isThrottled -> - if (isThrottled) { - when (interactor.getAuthenticationMethod()) { - is AuthenticationMethodModel.Pin -> - R.string.kg_too_many_failed_pin_attempts_dialog_message - is AuthenticationMethodModel.Password -> - R.string.kg_too_many_failed_password_attempts_dialog_message - is AuthenticationMethodModel.Pattern -> - R.string.kg_too_many_failed_pattern_attempts_dialog_message - else -> null - }?.let { stringResourceId -> - applicationContext.getString( - stringResourceId, - interactor.throttling.value.failedAttemptCount, - ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), - ) - } - } else { - null - } - } - .distinctUntilChanged() - .collect { dialogMessageOrNull -> - if (dialogMessageOrNull != null) { - _throttlingDialogMessage.value = dialogMessageOrNull - } - } - } - } - /** Notifies that the emergency services button was clicked. */ fun onEmergencyServicesButtonClicked() { // TODO(b/280877228): implement this diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt index c8f7efbeb397..1c200b086990 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt @@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.SceneModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Hosts business and application state accessing logic for the lockscreen scene. */ class LockscreenSceneInteractor @@ -42,7 +36,6 @@ constructor( @Application applicationScope: CoroutineScope, private val authenticationInteractor: AuthenticationInteractor, bouncerInteractorFactory: BouncerInteractor.Factory, - private val sceneInteractor: SceneInteractor, @Assisted private val containerName: String, ) { private val bouncerInteractor: BouncerInteractor = @@ -72,46 +65,6 @@ constructor( initialValue = false, ) - init { - // LOCKING SHOWS Lockscreen. - // - // Move to the lockscreen scene if the device becomes locked while in any scene. - applicationScope.launch { - authenticationInteractor.isUnlocked - .map { !it } - .distinctUntilChanged() - .collect { isLocked -> - if (isLocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Lockscreen), - ) - } - } - } - - // BYPASS UNLOCK. - // - // Moves to the gone scene if bypass is enabled and the device becomes unlocked while in the - // lockscreen scene. - applicationScope.launch { - combine( - authenticationInteractor.isBypassEnabled, - authenticationInteractor.isUnlocked, - sceneInteractor.currentScene(containerName), - ::Triple, - ) - .collect { (isBypassEnabled, isUnlocked, currentScene) -> - if (isBypassEnabled && isUnlocked && currentScene.key == SceneKey.Lockscreen) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) - } - } - } - } - /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ fun dismissLockscreen() { bouncerInteractor.showOrUnlockDevice(containerName = containerName) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt index 91400196bbb1..59f82f034723 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.domain.startable import com.android.systemui.CoreStartable +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags @@ -24,9 +25,11 @@ import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -41,17 +44,19 @@ class SystemUiDefaultSceneContainerStartable constructor( @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, + private val authenticationInteractor: AuthenticationInteractor, private val featureFlags: FeatureFlags, ) : CoreStartable { override fun start() { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { - keepVisibilityUpdated() + hydrateVisibility() + automaticallySwitchScenes() } } - /** Drives visibility of the scene container. */ - private fun keepVisibilityUpdated() { + /** Updates the visibility of the scene container based on the current scene. */ + private fun hydrateVisibility() { applicationScope.launch { sceneInteractor .currentScene(CONTAINER_NAME) @@ -63,6 +68,48 @@ constructor( } } + /** Switches between scenes based on ever-changing application state. */ + private fun automaticallySwitchScenes() { + applicationScope.launch { + authenticationInteractor.isUnlocked + .map { isUnlocked -> + val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key + val isBypassEnabled = authenticationInteractor.isBypassEnabled.value + when { + isUnlocked -> + when (currentSceneKey) { + // When the device becomes unlocked in Bouncer, go to the Gone. + is SceneKey.Bouncer -> SceneKey.Gone + // When the device becomes unlocked in Lockscreen, go to Gone if + // bypass is enabled. + is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled } + // We got unlocked while on a scene that's not Lockscreen or + // Bouncer, no need to change scenes. + else -> null + } + // When the device becomes locked, to Lockscreen. + !isUnlocked -> + when (currentSceneKey) { + // Already on lockscreen or bouncer, no need to change scenes. + is SceneKey.Lockscreen, + is SceneKey.Bouncer -> null + // We got locked while on a scene that's not Lockscreen or Bouncer, + // go to Lockscreen. + else -> SceneKey.Lockscreen + } + else -> null + } + } + .filterNotNull() + .collect { targetSceneKey -> + sceneInteractor.setCurrentScene( + containerName = CONTAINER_NAME, + scene = SceneModel(targetSceneKey), + ) + } + } + } + companion object { private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index c2219a4d82eb..481f36e8ea38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } - @Test - fun switchesToGone_whenUnlocked() = - testScope.runTest { - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene( - SceneTestUtils.CONTAINER_1, - SceneModel(SceneKey.Bouncer) - ) - val currentScene by - collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - - utils.authenticationRepository.setUnlocked(true) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - } - private fun assertTryAgainMessage( message: String?, time: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 0356036fa78f..0df0a17931f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -52,7 +52,10 @@ class BouncerViewModelTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, sceneInteractor = utils.sceneInteractor(), ) - private val underTest = utils.bouncerViewModel(bouncerInteractor) + private val underTest = + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, + ) @Test fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 7d6c4a1a1455..5c6d4c69b50b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -20,7 +20,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -54,16 +53,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { sceneInteractor = sceneInteractor, ) private val bouncerViewModel = - BouncerViewModel( - applicationContext = context, - applicationScope = testScope.backgroundScope, - interactorFactory = - object : BouncerInteractor.Factory { - override fun create(containerName: String): BouncerInteractor { - return bouncerInteractor - } - }, - containerName = SceneTestUtils.CONTAINER_1, + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, ) private val underTest = PinBouncerViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt index 8b36284b8620..ca6a5b6234b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt @@ -47,7 +47,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { private val underTest = utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -129,22 +128,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { } @Test - fun deviceLockedInNonLockScreenScene_switchesToLockScreenScene() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - runCurrent() - sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone)) - runCurrent() - utils.authenticationRepository.setUnlocked(true) - runCurrent() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - - utils.authenticationRepository.setUnlocked(false) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - } - - @Test fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() = testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 161dd660671e..ba8e0f277b6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -56,7 +56,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 4eedc99839c6..ed7a59ea7032 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -53,7 +53,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt index df3701edc6de..df5e7bcd16be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt @@ -14,14 +14,11 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) - package com.android.systemui.scene.domain.startable import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneContainerNames @@ -29,11 +26,14 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { @@ -41,29 +41,47 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val featureFlags = FakeFeatureFlags() + private val featureFlags = utils.featureFlags + private val authenticationRepository = utils.authenticationRepository() + private val authenticationInteractor = + utils.authenticationInteractor( + repository = authenticationRepository, + ) private val underTest = SystemUiDefaultSceneContainerStartable( applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, + authenticationInteractor = authenticationInteractor, featureFlags = featureFlags, ) + @Before + fun setUp() { + prepareState() + } + @Test - fun start_featureEnabled_keepsVisibilityUpdated() = + fun hydrateVisibility_featureEnabled() = testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, true) + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) val isVisible by collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) assertThat(isVisible).isTrue() underTest.start() - sceneInteractor.setCurrentScene( - SceneContainerNames.SYSTEM_UI_DEFAULT, - SceneModel(SceneKey.Gone) - ) assertThat(isVisible).isFalse() sceneInteractor.setCurrentScene( @@ -74,14 +92,26 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { } @Test - fun start_featureDisabled_doesNotUpdateVisibility() = + fun hydrateVisibility_featureDisabled() = testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, false) + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) val isVisible by collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) assertThat(isVisible).isTrue() underTest.start() + assertThat(isVisible).isTrue() sceneInteractor.setCurrentScene( SceneContainerNames.SYSTEM_UI_DEFAULT, @@ -95,4 +125,172 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { ) assertThat(isVisible).isTrue() } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = false, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + private fun prepareState( + isFeatureEnabled: Boolean = true, + isDeviceUnlocked: Boolean = false, + isBypassEnabled: Boolean = false, + initialSceneKey: SceneKey? = null, + ) { + featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled) + authenticationRepository.setUnlocked(isDeviceUnlocked) + authenticationRepository.setBypassEnabled(isBypassEnabled) + initialSceneKey?.let { + sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it)) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 309ab058fb64..6e9fba64263b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -22,7 +22,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.SceneTestUtils.Companion.CONTAINER_1 import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat @@ -54,7 +53,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -89,7 +87,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() @@ -102,7 +101,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index fec1187d8d11..d797962bae8b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -24,6 +24,8 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -51,6 +53,7 @@ class SceneTestUtils( ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) + val featureFlags = FakeFeatureFlags().apply { set(Flags.SCENE_CONTAINER, true) } private val userRepository: UserRepository by lazy { FakeUserRepository().apply { val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) @@ -129,6 +132,7 @@ class SceneTestUtils( repository = BouncerRepository(), authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } @@ -145,13 +149,13 @@ class SceneTestUtils( return bouncerInteractor } }, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } fun lockScreenSceneInteractor( authenticationInteractor: AuthenticationInteractor, - sceneInteractor: SceneInteractor, bouncerInteractor: BouncerInteractor, ): LockscreenSceneInteractor { return LockscreenSceneInteractor( @@ -163,7 +167,6 @@ class SceneTestUtils( return bouncerInteractor } }, - sceneInteractor = sceneInteractor, containerName = CONTAINER_1, ) } |