Loading packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +9 −31 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.dagger.qualifiers.Application 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.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.SceneModel Loading @@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine 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.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.launch Loading @@ -53,6 +52,7 @@ constructor( private val repository: BouncerRepository, private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, private val sceneInteractor: SceneInteractor, featureFlags: FeatureFlags, @Assisted private val containerName: String, @Assisted private val containerName: String, ) { ) { Loading Loading @@ -95,30 +95,7 @@ constructor( val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { init { // UNLOCKING SHOWS Gone. if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { // // 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), ) } } } // Clear the message if moved from throttling to no-longer throttling. // Clear the message if moved from throttling to no-longer throttling. applicationScope.launch { applicationScope.launch { isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> Loading @@ -128,6 +105,7 @@ constructor( } } } } } } } /** /** * Returns the currently-configured authentication method. This determines how the * Returns the currently-configured authentication method. This determines how the Loading packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +44 −41 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,8 @@ import com.android.systemui.R import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application 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 com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory Loading @@ -49,6 +51,7 @@ constructor( @Application private val applicationContext: Context, @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, interactorFactory: BouncerInteractor.Factory, interactorFactory: BouncerInteractor.Factory, featureFlags: FeatureFlags, @Assisted containerName: String, @Assisted containerName: String, ) { ) { private val interactor: BouncerInteractor = interactorFactory.create(containerName) private val interactor: BouncerInteractor = interactorFactory.create(containerName) Loading Loading @@ -102,6 +105,38 @@ constructor( ) ) init { init { 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 { applicationScope.launch { _authMethod.subscriptionCount _authMethod.subscriptionCount .pairwise() .pairwise() Loading @@ -113,6 +148,7 @@ constructor( } } } } } } } /** The user-facing message to show in the bouncer. */ /** The user-facing message to show in the bouncer. */ val message: StateFlow<MessageViewModel> = val message: StateFlow<MessageViewModel> = Loading Loading @@ -144,39 +180,6 @@ constructor( */ */ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() 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. */ /** Notifies that the emergency services button was clicked. */ fun onEmergencyServicesButtonClicked() { fun onEmergencyServicesButtonClicked() { // TODO(b/280877228): implement this // TODO(b/280877228): implement this Loading packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +0 −47 Original line number Original line Diff line number Diff line Loading @@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application 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.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** Hosts business and application state accessing logic for the lockscreen scene. */ /** Hosts business and application state accessing logic for the lockscreen scene. */ class LockscreenSceneInteractor class LockscreenSceneInteractor Loading @@ -42,7 +36,6 @@ constructor( @Application applicationScope: CoroutineScope, @Application applicationScope: CoroutineScope, private val authenticationInteractor: AuthenticationInteractor, private val authenticationInteractor: AuthenticationInteractor, bouncerInteractorFactory: BouncerInteractor.Factory, bouncerInteractorFactory: BouncerInteractor.Factory, private val sceneInteractor: SceneInteractor, @Assisted private val containerName: String, @Assisted private val containerName: String, ) { ) { private val bouncerInteractor: BouncerInteractor = private val bouncerInteractor: BouncerInteractor = Loading Loading @@ -72,46 +65,6 @@ constructor( initialValue = false, 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. */ /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ fun dismissLockscreen() { fun dismissLockscreen() { bouncerInteractor.showOrUnlockDevice(containerName = containerName) bouncerInteractor.showOrUnlockDevice(containerName = containerName) Loading packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +50 −3 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.scene.domain.startable package com.android.systemui.scene.domain.startable import com.android.systemui.CoreStartable import com.android.systemui.CoreStartable import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags Loading @@ -24,9 +25,11 @@ import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.launch Loading @@ -41,17 +44,19 @@ class SystemUiDefaultSceneContainerStartable constructor( constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, private val sceneInteractor: SceneInteractor, private val authenticationInteractor: AuthenticationInteractor, private val featureFlags: FeatureFlags, private val featureFlags: FeatureFlags, ) : CoreStartable { ) : CoreStartable { override fun start() { override fun start() { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { keepVisibilityUpdated() hydrateVisibility() automaticallySwitchScenes() } } } } /** Drives visibility of the scene container. */ /** Updates the visibility of the scene container based on the current scene. */ private fun keepVisibilityUpdated() { private fun hydrateVisibility() { applicationScope.launch { applicationScope.launch { sceneInteractor sceneInteractor .currentScene(CONTAINER_NAME) .currentScene(CONTAINER_NAME) Loading @@ -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 { companion object { private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT } } Loading packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +0 −17 Original line number Original line Diff line number Diff line Loading @@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) 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( private fun assertTryAgainMessage( message: String?, message: String?, time: Int, time: Int, Loading Loading
packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +9 −31 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.dagger.qualifiers.Application 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.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.scene.shared.model.SceneModel Loading @@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine 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.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.launch Loading @@ -53,6 +52,7 @@ constructor( private val repository: BouncerRepository, private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, private val sceneInteractor: SceneInteractor, featureFlags: FeatureFlags, @Assisted private val containerName: String, @Assisted private val containerName: String, ) { ) { Loading Loading @@ -95,30 +95,7 @@ constructor( val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { init { // UNLOCKING SHOWS Gone. if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { // // 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), ) } } } // Clear the message if moved from throttling to no-longer throttling. // Clear the message if moved from throttling to no-longer throttling. applicationScope.launch { applicationScope.launch { isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> Loading @@ -128,6 +105,7 @@ constructor( } } } } } } } /** /** * Returns the currently-configured authentication method. This determines how the * Returns the currently-configured authentication method. This determines how the Loading
packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +44 −41 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,8 @@ import com.android.systemui.R import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application 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 com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory Loading @@ -49,6 +51,7 @@ constructor( @Application private val applicationContext: Context, @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, interactorFactory: BouncerInteractor.Factory, interactorFactory: BouncerInteractor.Factory, featureFlags: FeatureFlags, @Assisted containerName: String, @Assisted containerName: String, ) { ) { private val interactor: BouncerInteractor = interactorFactory.create(containerName) private val interactor: BouncerInteractor = interactorFactory.create(containerName) Loading Loading @@ -102,6 +105,38 @@ constructor( ) ) init { init { 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 { applicationScope.launch { _authMethod.subscriptionCount _authMethod.subscriptionCount .pairwise() .pairwise() Loading @@ -113,6 +148,7 @@ constructor( } } } } } } } /** The user-facing message to show in the bouncer. */ /** The user-facing message to show in the bouncer. */ val message: StateFlow<MessageViewModel> = val message: StateFlow<MessageViewModel> = Loading Loading @@ -144,39 +180,6 @@ constructor( */ */ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() 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. */ /** Notifies that the emergency services button was clicked. */ fun onEmergencyServicesButtonClicked() { fun onEmergencyServicesButtonClicked() { // TODO(b/280877228): implement this // TODO(b/280877228): implement this Loading
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +0 −47 Original line number Original line Diff line number Diff line Loading @@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application 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.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** Hosts business and application state accessing logic for the lockscreen scene. */ /** Hosts business and application state accessing logic for the lockscreen scene. */ class LockscreenSceneInteractor class LockscreenSceneInteractor Loading @@ -42,7 +36,6 @@ constructor( @Application applicationScope: CoroutineScope, @Application applicationScope: CoroutineScope, private val authenticationInteractor: AuthenticationInteractor, private val authenticationInteractor: AuthenticationInteractor, bouncerInteractorFactory: BouncerInteractor.Factory, bouncerInteractorFactory: BouncerInteractor.Factory, private val sceneInteractor: SceneInteractor, @Assisted private val containerName: String, @Assisted private val containerName: String, ) { ) { private val bouncerInteractor: BouncerInteractor = private val bouncerInteractor: BouncerInteractor = Loading Loading @@ -72,46 +65,6 @@ constructor( initialValue = false, 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. */ /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ fun dismissLockscreen() { fun dismissLockscreen() { bouncerInteractor.showOrUnlockDevice(containerName = containerName) bouncerInteractor.showOrUnlockDevice(containerName = containerName) Loading
packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +50 −3 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.scene.domain.startable package com.android.systemui.scene.domain.startable import com.android.systemui.CoreStartable import com.android.systemui.CoreStartable import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags Loading @@ -24,9 +25,11 @@ import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.launch Loading @@ -41,17 +44,19 @@ class SystemUiDefaultSceneContainerStartable constructor( constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, private val sceneInteractor: SceneInteractor, private val authenticationInteractor: AuthenticationInteractor, private val featureFlags: FeatureFlags, private val featureFlags: FeatureFlags, ) : CoreStartable { ) : CoreStartable { override fun start() { override fun start() { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { keepVisibilityUpdated() hydrateVisibility() automaticallySwitchScenes() } } } } /** Drives visibility of the scene container. */ /** Updates the visibility of the scene container based on the current scene. */ private fun keepVisibilityUpdated() { private fun hydrateVisibility() { applicationScope.launch { applicationScope.launch { sceneInteractor sceneInteractor .currentScene(CONTAINER_NAME) .currentScene(CONTAINER_NAME) Loading @@ -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 { companion object { private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT } } Loading
packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +0 −17 Original line number Original line Diff line number Diff line Loading @@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) 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( private fun assertTryAgainMessage( message: String?, message: String?, time: Int, time: Int, Loading