Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt +7 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.fakeDeviceStateRepository import com.android.systemui.display.data.repository.fakeDeviceStateRepository import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.DisableSceneContainer Loading @@ -41,6 +42,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Before Loading Loading @@ -174,10 +176,15 @@ class LogContextInteractorImplTest : SysuiTestCase() { assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN) assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN) // Unlock the device. // Unlock the device. val isDeviceUnlocked by collectLastValue( kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } ) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) SuccessFingerprintAuthenticationStatus(0, true) ) ) runCurrent() runCurrent() assertThat(isDeviceUnlocked).isTrue() kosmos.sceneInteractor.snapToScene(Scenes.Gone, "") kosmos.sceneInteractor.snapToScene(Scenes.Gone, "") keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE) keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt +7 −0 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus Loading @@ -48,6 +49,7 @@ import com.google.common.truth.Truth.assertThat import java.util.Locale import java.util.Locale import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent Loading Loading @@ -210,10 +212,15 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() { private fun TestScope.setDeviceEntered(isEntered: Boolean) { private fun TestScope.setDeviceEntered(isEntered: Boolean) { if (isEntered) { if (isEntered) { // Unlock the device marking the device has entered. // Unlock the device marking the device has entered. val isDeviceUnlocked by collectLastValue( kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } ) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) SuccessFingerprintAuthenticationStatus(0, true) ) ) runCurrent() runCurrent() assertThat(isDeviceUnlocked).isTrue() } } setScene( setScene( if (isEntered) { if (isEntered) { Loading packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +51 −36 Original line number Original line Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.pairwise import com.android.systemui.utils.coroutines.flow.mapLatestConflated import com.android.systemui.utils.coroutines.flow.mapLatestConflated import dagger.Lazy import javax.inject.Inject import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow Loading @@ -59,14 +60,14 @@ class DeviceEntryInteractor @Inject @Inject constructor( constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, private val repository: DeviceEntryRepository, private val repository: Lazy<DeviceEntryRepository>, private val authenticationInteractor: AuthenticationInteractor, private val authenticationInteractor: Lazy<AuthenticationInteractor>, private val sceneInteractor: SceneInteractor, private val sceneInteractor: Lazy<SceneInteractor>, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>, private val dismissCallbackRegistry: DismissCallbackRegistry, private val dismissCallbackRegistry: Lazy<DismissCallbackRegistry>, sceneBackInteractor: SceneBackInteractor, sceneBackInteractor: Lazy<SceneBackInteractor>, @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer, @SceneFrameworkTableLog private val tableLogBuffer: Lazy<TableLogBuffer>, ) { ) { /** /** * Whether the device is unlocked. * Whether the device is unlocked. Loading @@ -77,14 +78,17 @@ constructor( * of this flow will always be `true`, even if the lockscreen is showing and still needs to be * of this flow will always be `true`, even if the lockscreen is showing and still needs to be * dismissed by the user to proceed. * dismissed by the user to proceed. */ */ val isUnlocked: StateFlow<Boolean> = val isUnlocked: StateFlow<Boolean> by lazy { deviceUnlockedInteractor.deviceUnlockStatus deviceUnlockedInteractor .get() .deviceUnlockStatus .map { it.isUnlocked } .map { it.isUnlocked } .stateIn( .stateIn( scope = applicationScope, scope = applicationScope, started = SharingStarted.WhileSubscribed(), started = SharingStarted.WhileSubscribed(), initialValue = deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked, initialValue = deviceUnlockedInteractor.get().deviceUnlockStatus.value.isUnlocked, ) ) } /** /** * Emits `true` when the current scene switches to [Scenes.Gone] for the first time after having * Emits `true` when the current scene switches to [Scenes.Gone] for the first time after having Loading @@ -96,8 +100,10 @@ constructor( * [Scenes.Gone] but the bottommost entry of the navigation back stack switched from * [Scenes.Gone] but the bottommost entry of the navigation back stack switched from * [Scenes.Lockscreen] to [Scenes.Gone] while the user is staring at another scene. * [Scenes.Lockscreen] to [Scenes.Gone] while the user is staring at another scene. */ */ val isDeviceEnteredDirectly: StateFlow<Boolean> = val isDeviceEnteredDirectly: StateFlow<Boolean> by lazy { sceneInteractor.currentScene sceneInteractor .get() .currentScene .filter { currentScene -> .filter { currentScene -> currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen } } Loading @@ -105,7 +111,7 @@ constructor( if (scene == Scenes.Gone) { if (scene == Scenes.Gone) { // Make sure device unlock status is definitely unlocked before we // Make sure device unlock status is definitely unlocked before we // consider the device "entered". // consider the device "entered". deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } deviceUnlockedInteractor.get().deviceUnlockStatus.first { it.isUnlocked } true true } else { } else { false false Loading @@ -116,6 +122,7 @@ constructor( started = SharingStarted.Eagerly, started = SharingStarted.Eagerly, initialValue = false, initialValue = false, ) ) } /** /** * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). Loading @@ -129,7 +136,7 @@ constructor( * navigation back stack into account and will only produce a `true` value even when the current * navigation back stack into account and will only produce a `true` value even when the current * scene is actually [Scenes.Gone]. * scene is actually [Scenes.Gone]. */ */ val isDeviceEntered: StateFlow<Boolean> = val isDeviceEntered: StateFlow<Boolean> by lazy { combine( combine( // This flow emits true when the currentScene switches to Gone for the first time // This flow emits true when the currentScene switches to Gone for the first time // after having been on Lockscreen. // after having been on Lockscreen. Loading @@ -137,7 +144,9 @@ constructor( // This flow emits true only if the bottom of the navigation back stack has been // This flow emits true only if the bottom of the navigation back stack has been // switched from Lockscreen to Gone. In other words, only if the device was unlocked // switched from Lockscreen to Gone. In other words, only if the device was unlocked // while visiting at least one scene "above" the Lockscreen scene. // while visiting at least one scene "above" the Lockscreen scene. sceneBackInteractor.backStack sceneBackInteractor .get() .backStack // The bottom of the back stack, which is Lockscreen, Gone, or null if empty. // The bottom of the back stack, which is Lockscreen, Gone, or null if empty. .map { it.asIterable().lastOrNull() } .map { it.asIterable().lastOrNull() } // Filter out cases where the stack changes but the bottom remains unchanged. // Filter out cases where the stack changes but the bottom remains unchanged. Loading @@ -153,7 +162,7 @@ constructor( enteredOnBackStack || enteredDirectly enteredOnBackStack || enteredDirectly } } .logDiffsForTable( .logDiffsForTable( tableLogBuffer = tableLogBuffer, tableLogBuffer = tableLogBuffer.get(), columnName = "isDeviceEntered", columnName = "isDeviceEntered", initialValue = false, initialValue = false, ) ) Loading @@ -162,9 +171,10 @@ constructor( started = SharingStarted.Eagerly, started = SharingStarted.Eagerly, initialValue = false, initialValue = false, ) ) } val isLockscreenEnabled: Flow<Boolean> by lazy { val isLockscreenEnabled: Flow<Boolean> by lazy { repository.isLockscreenEnabled.onStart { refreshLockscreenEnabled() } repository.get().isLockscreenEnabled.onStart { refreshLockscreenEnabled() } } } /** /** Loading @@ -181,11 +191,11 @@ constructor( */ */ val canSwipeToEnter: StateFlow<Boolean?> by lazy { val canSwipeToEnter: StateFlow<Boolean?> by lazy { combine( combine( authenticationInteractor.authenticationMethod.map { authenticationInteractor.get().authenticationMethod.map { it == AuthenticationMethodModel.None it == AuthenticationMethodModel.None }, }, isLockscreenEnabled, isLockscreenEnabled, deviceUnlockedInteractor.deviceUnlockStatus, deviceUnlockedInteractor.get().deviceUnlockStatus, isDeviceEntered, isDeviceEntered, ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered -> ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered -> val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled Loading @@ -195,7 +205,7 @@ constructor( !isDeviceEntered !isDeviceEntered } } .logDiffsForTable( .logDiffsForTable( tableLogBuffer = tableLogBuffer, tableLogBuffer = tableLogBuffer.get(), columnName = "canSwipeToEnter", columnName = "canSwipeToEnter", initialValue = false, initialValue = false, ) ) Loading @@ -218,7 +228,7 @@ constructor( */ */ @JvmOverloads @JvmOverloads fun attemptDeviceEntry(callback: IKeyguardDismissCallback? = null) { fun attemptDeviceEntry(callback: IKeyguardDismissCallback? = null) { callback?.let { dismissCallbackRegistry.addCallback(it) } callback?.let { dismissCallbackRegistry.get().addCallback(it) } // TODO (b/307768356), // TODO (b/307768356), // 1. Check if the device is already authenticated by trust agent/passive biometrics // 1. Check if the device is already authenticated by trust agent/passive biometrics Loading @@ -228,18 +238,23 @@ constructor( // 4. Transition to bouncer scene // 4. Transition to bouncer scene applicationScope.launch { applicationScope.launch { if (isAuthenticationRequired()) { if (isAuthenticationRequired()) { if (alternateBouncerInteractor.canShowAlternateBouncer.value) { if (alternateBouncerInteractor.get().canShowAlternateBouncer.value) { alternateBouncerInteractor.forceShow() alternateBouncerInteractor.get().forceShow() } else { } else { sceneInteractor.showOverlay( sceneInteractor .get() .showOverlay( overlay = Overlays.Bouncer, overlay = Overlays.Bouncer, loggingReason = "request to unlock device while authentication required", loggingReason = "request to unlock device while authentication required", ) ) } } } else { } else { sceneInteractor.changeScene( sceneInteractor .get() .changeScene( toScene = Scenes.Gone, toScene = Scenes.Gone, loggingReason = "request to unlock device while authentication isn't required", loggingReason = "request to unlock device while authentication isn't required", ) ) } } } } Loading @@ -250,8 +265,8 @@ constructor( * `false` if the device can be entered without authenticating first. * `false` if the device can be entered without authenticating first. */ */ suspend fun isAuthenticationRequired(): Boolean { suspend fun isAuthenticationRequired(): Boolean { return !deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked && return !deviceUnlockedInteractor.get().deviceUnlockStatus.value.isUnlocked && authenticationInteractor.getAuthenticationMethod().isSecure authenticationInteractor.get().getAuthenticationMethod().isSecure } } /** /** Loading @@ -260,7 +275,7 @@ constructor( * when the user swipes on it. * when the user swipes on it. */ */ suspend fun isLockscreenEnabled(): Boolean { suspend fun isLockscreenEnabled(): Boolean { return repository.isLockscreenEnabled() return repository.get().isLockscreenEnabled() } } /** /** Loading @@ -276,6 +291,6 @@ constructor( /** Locks the device instantly. */ /** Locks the device instantly. */ fun lockNow(debuggingReason: String) { fun lockNow(debuggingReason: String) { deviceUnlockedInteractor.lockNow(debuggingReason) deviceUnlockedInteractor.get().lockNow(debuggingReason) } } } } packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt +8 −8 Original line number Original line Diff line number Diff line Loading @@ -30,13 +30,13 @@ val Kosmos.deviceEntryInteractor by Kosmos.Fixture { Kosmos.Fixture { DeviceEntryInteractor( DeviceEntryInteractor( applicationScope = applicationCoroutineScope, applicationScope = applicationCoroutineScope, repository = deviceEntryRepository, repository = { deviceEntryRepository }, authenticationInteractor = authenticationInteractor, authenticationInteractor = { authenticationInteractor }, sceneInteractor = sceneInteractor, sceneInteractor = { sceneInteractor }, deviceUnlockedInteractor = deviceUnlockedInteractor, deviceUnlockedInteractor = { deviceUnlockedInteractor }, alternateBouncerInteractor = alternateBouncerInteractor, alternateBouncerInteractor = { alternateBouncerInteractor }, dismissCallbackRegistry = dismissCallbackRegistry, dismissCallbackRegistry = { dismissCallbackRegistry }, sceneBackInteractor = sceneBackInteractor, sceneBackInteractor = { sceneBackInteractor }, tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"), tableLogBuffer = { logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer") }, ) ) } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt +7 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.fakeDeviceStateRepository import com.android.systemui.display.data.repository.fakeDeviceStateRepository import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.DisableSceneContainer Loading @@ -41,6 +42,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Before Loading Loading @@ -174,10 +176,15 @@ class LogContextInteractorImplTest : SysuiTestCase() { assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN) assertThat(displayState()).isEqualTo(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN) // Unlock the device. // Unlock the device. val isDeviceUnlocked by collectLastValue( kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } ) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) SuccessFingerprintAuthenticationStatus(0, true) ) ) runCurrent() runCurrent() assertThat(isDeviceUnlocked).isTrue() kosmos.sceneInteractor.snapToScene(Scenes.Gone, "") kosmos.sceneInteractor.snapToScene(Scenes.Gone, "") keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE) keyguardTransitionRepository.startTransitionTo(KeyguardState.GONE) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt +7 −0 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus Loading @@ -48,6 +49,7 @@ import com.google.common.truth.Truth.assertThat import java.util.Locale import java.util.Locale import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent Loading Loading @@ -210,10 +212,15 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() { private fun TestScope.setDeviceEntered(isEntered: Boolean) { private fun TestScope.setDeviceEntered(isEntered: Boolean) { if (isEntered) { if (isEntered) { // Unlock the device marking the device has entered. // Unlock the device marking the device has entered. val isDeviceUnlocked by collectLastValue( kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } ) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) SuccessFingerprintAuthenticationStatus(0, true) ) ) runCurrent() runCurrent() assertThat(isDeviceUnlocked).isTrue() } } setScene( setScene( if (isEntered) { if (isEntered) { Loading
packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +51 −36 Original line number Original line Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.pairwise import com.android.systemui.utils.coroutines.flow.mapLatestConflated import com.android.systemui.utils.coroutines.flow.mapLatestConflated import dagger.Lazy import javax.inject.Inject import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow Loading @@ -59,14 +60,14 @@ class DeviceEntryInteractor @Inject @Inject constructor( constructor( @Application private val applicationScope: CoroutineScope, @Application private val applicationScope: CoroutineScope, private val repository: DeviceEntryRepository, private val repository: Lazy<DeviceEntryRepository>, private val authenticationInteractor: AuthenticationInteractor, private val authenticationInteractor: Lazy<AuthenticationInteractor>, private val sceneInteractor: SceneInteractor, private val sceneInteractor: Lazy<SceneInteractor>, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>, private val dismissCallbackRegistry: DismissCallbackRegistry, private val dismissCallbackRegistry: Lazy<DismissCallbackRegistry>, sceneBackInteractor: SceneBackInteractor, sceneBackInteractor: Lazy<SceneBackInteractor>, @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer, @SceneFrameworkTableLog private val tableLogBuffer: Lazy<TableLogBuffer>, ) { ) { /** /** * Whether the device is unlocked. * Whether the device is unlocked. Loading @@ -77,14 +78,17 @@ constructor( * of this flow will always be `true`, even if the lockscreen is showing and still needs to be * of this flow will always be `true`, even if the lockscreen is showing and still needs to be * dismissed by the user to proceed. * dismissed by the user to proceed. */ */ val isUnlocked: StateFlow<Boolean> = val isUnlocked: StateFlow<Boolean> by lazy { deviceUnlockedInteractor.deviceUnlockStatus deviceUnlockedInteractor .get() .deviceUnlockStatus .map { it.isUnlocked } .map { it.isUnlocked } .stateIn( .stateIn( scope = applicationScope, scope = applicationScope, started = SharingStarted.WhileSubscribed(), started = SharingStarted.WhileSubscribed(), initialValue = deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked, initialValue = deviceUnlockedInteractor.get().deviceUnlockStatus.value.isUnlocked, ) ) } /** /** * Emits `true` when the current scene switches to [Scenes.Gone] for the first time after having * Emits `true` when the current scene switches to [Scenes.Gone] for the first time after having Loading @@ -96,8 +100,10 @@ constructor( * [Scenes.Gone] but the bottommost entry of the navigation back stack switched from * [Scenes.Gone] but the bottommost entry of the navigation back stack switched from * [Scenes.Lockscreen] to [Scenes.Gone] while the user is staring at another scene. * [Scenes.Lockscreen] to [Scenes.Gone] while the user is staring at another scene. */ */ val isDeviceEnteredDirectly: StateFlow<Boolean> = val isDeviceEnteredDirectly: StateFlow<Boolean> by lazy { sceneInteractor.currentScene sceneInteractor .get() .currentScene .filter { currentScene -> .filter { currentScene -> currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen } } Loading @@ -105,7 +111,7 @@ constructor( if (scene == Scenes.Gone) { if (scene == Scenes.Gone) { // Make sure device unlock status is definitely unlocked before we // Make sure device unlock status is definitely unlocked before we // consider the device "entered". // consider the device "entered". deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } deviceUnlockedInteractor.get().deviceUnlockStatus.first { it.isUnlocked } true true } else { } else { false false Loading @@ -116,6 +122,7 @@ constructor( started = SharingStarted.Eagerly, started = SharingStarted.Eagerly, initialValue = false, initialValue = false, ) ) } /** /** * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). Loading @@ -129,7 +136,7 @@ constructor( * navigation back stack into account and will only produce a `true` value even when the current * navigation back stack into account and will only produce a `true` value even when the current * scene is actually [Scenes.Gone]. * scene is actually [Scenes.Gone]. */ */ val isDeviceEntered: StateFlow<Boolean> = val isDeviceEntered: StateFlow<Boolean> by lazy { combine( combine( // This flow emits true when the currentScene switches to Gone for the first time // This flow emits true when the currentScene switches to Gone for the first time // after having been on Lockscreen. // after having been on Lockscreen. Loading @@ -137,7 +144,9 @@ constructor( // This flow emits true only if the bottom of the navigation back stack has been // This flow emits true only if the bottom of the navigation back stack has been // switched from Lockscreen to Gone. In other words, only if the device was unlocked // switched from Lockscreen to Gone. In other words, only if the device was unlocked // while visiting at least one scene "above" the Lockscreen scene. // while visiting at least one scene "above" the Lockscreen scene. sceneBackInteractor.backStack sceneBackInteractor .get() .backStack // The bottom of the back stack, which is Lockscreen, Gone, or null if empty. // The bottom of the back stack, which is Lockscreen, Gone, or null if empty. .map { it.asIterable().lastOrNull() } .map { it.asIterable().lastOrNull() } // Filter out cases where the stack changes but the bottom remains unchanged. // Filter out cases where the stack changes but the bottom remains unchanged. Loading @@ -153,7 +162,7 @@ constructor( enteredOnBackStack || enteredDirectly enteredOnBackStack || enteredDirectly } } .logDiffsForTable( .logDiffsForTable( tableLogBuffer = tableLogBuffer, tableLogBuffer = tableLogBuffer.get(), columnName = "isDeviceEntered", columnName = "isDeviceEntered", initialValue = false, initialValue = false, ) ) Loading @@ -162,9 +171,10 @@ constructor( started = SharingStarted.Eagerly, started = SharingStarted.Eagerly, initialValue = false, initialValue = false, ) ) } val isLockscreenEnabled: Flow<Boolean> by lazy { val isLockscreenEnabled: Flow<Boolean> by lazy { repository.isLockscreenEnabled.onStart { refreshLockscreenEnabled() } repository.get().isLockscreenEnabled.onStart { refreshLockscreenEnabled() } } } /** /** Loading @@ -181,11 +191,11 @@ constructor( */ */ val canSwipeToEnter: StateFlow<Boolean?> by lazy { val canSwipeToEnter: StateFlow<Boolean?> by lazy { combine( combine( authenticationInteractor.authenticationMethod.map { authenticationInteractor.get().authenticationMethod.map { it == AuthenticationMethodModel.None it == AuthenticationMethodModel.None }, }, isLockscreenEnabled, isLockscreenEnabled, deviceUnlockedInteractor.deviceUnlockStatus, deviceUnlockedInteractor.get().deviceUnlockStatus, isDeviceEntered, isDeviceEntered, ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered -> ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered -> val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled Loading @@ -195,7 +205,7 @@ constructor( !isDeviceEntered !isDeviceEntered } } .logDiffsForTable( .logDiffsForTable( tableLogBuffer = tableLogBuffer, tableLogBuffer = tableLogBuffer.get(), columnName = "canSwipeToEnter", columnName = "canSwipeToEnter", initialValue = false, initialValue = false, ) ) Loading @@ -218,7 +228,7 @@ constructor( */ */ @JvmOverloads @JvmOverloads fun attemptDeviceEntry(callback: IKeyguardDismissCallback? = null) { fun attemptDeviceEntry(callback: IKeyguardDismissCallback? = null) { callback?.let { dismissCallbackRegistry.addCallback(it) } callback?.let { dismissCallbackRegistry.get().addCallback(it) } // TODO (b/307768356), // TODO (b/307768356), // 1. Check if the device is already authenticated by trust agent/passive biometrics // 1. Check if the device is already authenticated by trust agent/passive biometrics Loading @@ -228,18 +238,23 @@ constructor( // 4. Transition to bouncer scene // 4. Transition to bouncer scene applicationScope.launch { applicationScope.launch { if (isAuthenticationRequired()) { if (isAuthenticationRequired()) { if (alternateBouncerInteractor.canShowAlternateBouncer.value) { if (alternateBouncerInteractor.get().canShowAlternateBouncer.value) { alternateBouncerInteractor.forceShow() alternateBouncerInteractor.get().forceShow() } else { } else { sceneInteractor.showOverlay( sceneInteractor .get() .showOverlay( overlay = Overlays.Bouncer, overlay = Overlays.Bouncer, loggingReason = "request to unlock device while authentication required", loggingReason = "request to unlock device while authentication required", ) ) } } } else { } else { sceneInteractor.changeScene( sceneInteractor .get() .changeScene( toScene = Scenes.Gone, toScene = Scenes.Gone, loggingReason = "request to unlock device while authentication isn't required", loggingReason = "request to unlock device while authentication isn't required", ) ) } } } } Loading @@ -250,8 +265,8 @@ constructor( * `false` if the device can be entered without authenticating first. * `false` if the device can be entered without authenticating first. */ */ suspend fun isAuthenticationRequired(): Boolean { suspend fun isAuthenticationRequired(): Boolean { return !deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked && return !deviceUnlockedInteractor.get().deviceUnlockStatus.value.isUnlocked && authenticationInteractor.getAuthenticationMethod().isSecure authenticationInteractor.get().getAuthenticationMethod().isSecure } } /** /** Loading @@ -260,7 +275,7 @@ constructor( * when the user swipes on it. * when the user swipes on it. */ */ suspend fun isLockscreenEnabled(): Boolean { suspend fun isLockscreenEnabled(): Boolean { return repository.isLockscreenEnabled() return repository.get().isLockscreenEnabled() } } /** /** Loading @@ -276,6 +291,6 @@ constructor( /** Locks the device instantly. */ /** Locks the device instantly. */ fun lockNow(debuggingReason: String) { fun lockNow(debuggingReason: String) { deviceUnlockedInteractor.lockNow(debuggingReason) deviceUnlockedInteractor.get().lockNow(debuggingReason) } } } }
packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt +8 −8 Original line number Original line Diff line number Diff line Loading @@ -30,13 +30,13 @@ val Kosmos.deviceEntryInteractor by Kosmos.Fixture { Kosmos.Fixture { DeviceEntryInteractor( DeviceEntryInteractor( applicationScope = applicationCoroutineScope, applicationScope = applicationCoroutineScope, repository = deviceEntryRepository, repository = { deviceEntryRepository }, authenticationInteractor = authenticationInteractor, authenticationInteractor = { authenticationInteractor }, sceneInteractor = sceneInteractor, sceneInteractor = { sceneInteractor }, deviceUnlockedInteractor = deviceUnlockedInteractor, deviceUnlockedInteractor = { deviceUnlockedInteractor }, alternateBouncerInteractor = alternateBouncerInteractor, alternateBouncerInteractor = { alternateBouncerInteractor }, dismissCallbackRegistry = dismissCallbackRegistry, dismissCallbackRegistry = { dismissCallbackRegistry }, sceneBackInteractor = sceneBackInteractor, sceneBackInteractor = { sceneBackInteractor }, tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"), tableLogBuffer = { logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer") }, ) ) } }