diff options
| author | 2022-10-28 20:24:40 +0000 | |
|---|---|---|
| committer | 2022-11-02 16:07:49 +0000 | |
| commit | 01d160afd693b0b5a7e6567734a96c95b204dce9 (patch) | |
| tree | d2d2c53a7ce9c30a6716f89dc76775304540897b | |
| parent | 847d0f53e7e84c181b75f7fe1fc0c057c574758d (diff) | |
Transitions - Add more source data, refine existing transitions
Data sources - Listen for events from the BiometricUnlockController
and KeyguardStateController#isBouncerShowing
Repositories - Add isBouncerShowing and WakeAndUnlock events into
KeyguardRepository
Interactors - Refine keyguard transitions so that states are more
accurate
BiometricUnlockController - Allow multiple listeners so that events
can make it into the KeyguardRepository. Cleanup redundant callback.
Test: ClockEventControllerTest KeyguardRepositoryImplTest
KeyguardTransitionRepositoryTest KeyguardTransitionInteractorTest
BiometricsUnlockControllerTest CentralSurfacesImplTest
Bug: 195430376
Change-Id: I983277fd9764d88c078bbf6da117cec3dd12dede
18 files changed, 454 insertions, 64 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index d48d7ffcd824..c9b8712bdde9 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -228,7 +228,7 @@ open class ClockEventController @Inject constructor( listenForDozing(this) if (featureFlags.isEnabled(DOZING_MIGRATION_1)) { listenForDozeAmountTransition(this) - listenForGoneToAodTransition(this) + listenForAnyStateToAodTransition(this) } else { listenForDozeAmount(this) } @@ -286,10 +286,10 @@ open class ClockEventController @Inject constructor( * dozing. */ @VisibleForTesting - internal fun listenForGoneToAodTransition(scope: CoroutineScope): Job { + internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job { return scope.launch { - keyguardTransitionInteractor.goneToAodTransition.filter { - it.transitionState == TransitionState.STARTED + keyguardTransitionInteractor.anyStateToAodTransition.filter { + it.transitionState == TransitionState.FINISHED }.collect { dozeAmount = 1f clock?.animations?.doze(dozeAmount) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 6baaf5f10024..c867c6e9229b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -23,9 +23,12 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.doze.DozeHost import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.phone.BiometricUnlockController +import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose @@ -65,6 +68,9 @@ interface KeyguardRepository { */ val isKeyguardShowing: Flow<Boolean> + /** Observable for whether the bouncer is showing. */ + val isBouncerShowing: Flow<Boolean> + /** * Observable for whether we are in doze state. * @@ -95,6 +101,9 @@ interface KeyguardRepository { /** Observable for device wake/sleep state */ val wakefulnessState: Flow<WakefulnessModel> + /** Observable for biometric unlock modes */ + val biometricUnlockState: Flow<BiometricUnlockModel> + /** * Returns `true` if the keyguard is showing; `false` otherwise. * @@ -125,6 +134,7 @@ constructor( private val keyguardStateController: KeyguardStateController, dozeHost: DozeHost, wakefulnessLifecycle: WakefulnessLifecycle, + biometricUnlockController: BiometricUnlockController, ) : KeyguardRepository { private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) override val animateBottomAreaDozingTransitions = @@ -159,6 +169,29 @@ constructor( awaitClose { keyguardStateController.removeCallback(callback) } } + override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow { + val callback = + object : KeyguardStateController.Callback { + override fun onBouncerShowingChanged() { + trySendWithFailureLogging( + keyguardStateController.isBouncerShowing, + TAG, + "updated isBouncerShowing" + ) + } + } + + keyguardStateController.addCallback(callback) + // Adding the callback does not send an initial update. + trySendWithFailureLogging( + keyguardStateController.isBouncerShowing, + TAG, + "initial isBouncerShowing" + ) + + awaitClose { keyguardStateController.removeCallback(callback) } + } + override val isDozing: Flow<Boolean> = conflatedCallbackFlow { val callback = @@ -248,6 +281,24 @@ constructor( awaitClose { wakefulnessLifecycle.removeObserver(callback) } } + override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow { + val callback = + object : BiometricUnlockController.BiometricModeListener { + override fun onModeChanged(@WakeAndUnlockMode mode: Int) { + trySendWithFailureLogging(biometricModeIntToObject(mode), TAG, "biometric mode") + } + } + + biometricUnlockController.addBiometricModeListener(callback) + trySendWithFailureLogging( + biometricModeIntToObject(biometricUnlockController.getMode()), + TAG, + "initial biometric mode" + ) + + awaitClose { biometricUnlockController.removeBiometricModeListener(callback) } + } + override fun setAnimateDozingTransitions(animate: Boolean) { _animateBottomAreaDozingTransitions.value = animate } @@ -279,6 +330,20 @@ constructor( } } + private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel { + return when (value) { + 0 -> BiometricUnlockModel.NONE + 1 -> BiometricUnlockModel.WAKE_AND_UNLOCK + 2 -> BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING + 3 -> BiometricUnlockModel.SHOW_BOUNCER + 4 -> BiometricUnlockModel.ONLY_WAKE + 5 -> BiometricUnlockModel.UNLOCK_COLLAPSING + 6 -> BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM + 7 -> BiometricUnlockModel.DISMISS_BOUNCER + else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value") + } + } + companion object { private const val TAG = "KeyguardRepositoryImpl" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt new file mode 100644 index 000000000000..7e01db34319f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.animation.ValueAnimator +import com.android.systemui.animation.Interpolators +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.WAKE_AND_UNLOCK +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionInfo +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch + +@SysUISingleton +class AodToGoneTransitionInteractor +@Inject +constructor( + @Application private val scope: CoroutineScope, + private val keyguardInteractor: KeyguardInteractor, + private val keyguardTransitionRepository: KeyguardTransitionRepository, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, +) : TransitionInteractor("AOD->GONE") { + + private val wakeAndUnlockModes = + setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING) + + override fun start() { + scope.launch { + keyguardInteractor.biometricUnlockState + .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) + .collect { pair -> + val (biometricUnlockState, keyguardState) = pair + if ( + keyguardState == KeyguardState.AOD && + wakeAndUnlockModes.contains(biometricUnlockState) + ) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + KeyguardState.AOD, + KeyguardState.GONE, + getAnimator(), + ) + ) + } + } + } + } + + private fun getAnimator(): ValueAnimator { + return ValueAnimator().apply { + setInterpolator(Interpolators.LINEAR) + setDuration(TRANSITION_DURATION_MS) + } + } + + companion object { + private const val TRANSITION_DURATION_MS = 500L + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 03c6a789326e..614ff8d930d8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -19,6 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -39,10 +41,19 @@ constructor( val dozeAmount: Flow<Float> = repository.dozeAmount /** Whether the system is in doze mode. */ val isDozing: Flow<Boolean> = repository.isDozing - /** Whether the keyguard is showing to not. */ + /** Whether the keyguard is showing or not. */ val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing + /** Whether the bouncer is showing or not. */ + val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing /** The device wake/sleep state */ val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState + /** Observable for the [StatusBarState] */ + val statusBarState: Flow<StatusBarState> = repository.statusBarState + /** + * Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear, + * side, under display) is used to unlock the device. + */ + val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState fun isKeyguardShowing(): Boolean { return repository.isKeyguardShowing() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index 83d94325b9d4..57fb4a114700 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -41,11 +41,13 @@ constructor( } scope.launch { - interactor.finishedKeyguardState.collect { logger.i("Finished transition to", it) } + interactor.finishedKeyguardTransitionStep.collect { + logger.i("Finished transition", it) + } } scope.launch { - interactor.startedKeyguardState.collect { logger.i("Started transition to", it) } + interactor.startedKeyguardTransitionStep.collect { logger.i("Started transition", it) } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt index d5ea77b8e729..a7c6d4450336 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt @@ -41,6 +41,7 @@ constructor( is AodLockscreenTransitionInteractor -> Log.d(TAG, "Started $it") is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it") is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it") + is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it") } it.start() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index dffd097a77c5..749183e241e5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -21,7 +21,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD -import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -44,8 +43,9 @@ constructor( /** LOCKSCREEN->AOD transition information. */ val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD) - /** GONE->AOD information. */ - val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD) + /** (any)->AOD transition information */ + val anyStateToAodTransition: Flow<TransitionStep> = + repository.transitions.filter { step -> step.to == KeyguardState.AOD } /** * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> @@ -57,15 +57,15 @@ constructor( lockscreenToAodTransition, ) + /* The last [TransitionStep] with a [TransitionState] of FINISHED */ + val finishedKeyguardTransitionStep: Flow<TransitionStep> = + repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED } + /* The last completed [KeyguardState] transition */ val finishedKeyguardState: Flow<KeyguardState> = - repository.transitions - .filter { step -> step.transitionState == TransitionState.FINISHED } - .map { step -> step.to } + finishedKeyguardTransitionStep.map { step -> step.to } - /* The last started [KeyguardState] transition */ - val startedKeyguardState: Flow<KeyguardState> = - repository.transitions - .filter { step -> step.transitionState == TransitionState.STARTED } - .map { step -> step.to } + /* The last [TransitionStep] with a [TransitionState] of STARTED */ + val startedKeyguardTransitionStep: Flow<TransitionStep> = + repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt index 761f3fd9d9f9..fd4814d2bc94 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt @@ -16,14 +16,16 @@ package com.android.systemui.keyguard.domain.interactor +import android.animation.ValueAnimator +import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.sample import java.util.UUID @@ -38,7 +40,7 @@ class LockscreenBouncerTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, - private val keyguardRepository: KeyguardRepository, + private val keyguardInteractor: KeyguardInteractor, private val shadeRepository: ShadeRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor @@ -47,12 +49,47 @@ constructor( private var transitionId: UUID? = null override fun start() { + listenForDraggingUpToBouncer() + listenForBouncerHiding() + } + + private fun listenForBouncerHiding() { + scope.launch { + keyguardInteractor.isBouncerShowing + .sample(keyguardInteractor.wakefulnessState, { a, b -> Pair(a, b) }) + .collect { pair -> + val (isBouncerShowing, wakefulnessState) = pair + if (!isBouncerShowing) { + val to = + if ( + wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP || + wakefulnessState == WakefulnessModel.ASLEEP + ) { + KeyguardState.AOD + } else { + KeyguardState.LOCKSCREEN + } + keyguardTransitionRepository.startTransition( + TransitionInfo( + ownerName = name, + from = KeyguardState.BOUNCER, + to = to, + animator = getAnimator(), + ) + ) + } + } + } + } + + /* Starts transitions when manually dragging up the bouncer from the lockscreen. */ + private fun listenForDraggingUpToBouncer() { scope.launch { shadeRepository.shadeModel .sample( combine( keyguardTransitionInteractor.finishedKeyguardState, - keyguardRepository.statusBarState, + keyguardInteractor.statusBarState, ) { keyguardState, statusBarState -> Pair(keyguardState, statusBarState) }, @@ -100,4 +137,15 @@ constructor( } } } + + private fun getAnimator(): ValueAnimator { + return ValueAnimator().apply { + setInterpolator(Interpolators.LINEAR) + setDuration(TRANSITION_DURATION_MS) + } + } + + companion object { + private const val TRANSITION_DURATION_MS = 300L + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt index 728bafae2a4c..37f33afbf53e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt @@ -42,6 +42,8 @@ abstract class StartKeyguardTransitionModule { @Binds @IntoSet abstract fun goneAod(impl: GoneAodTransitionInteractor): TransitionInteractor + @Binds @IntoSet abstract fun aodGone(impl: AodToGoneTransitionInteractor): TransitionInteractor + @Binds @IntoSet abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt new file mode 100644 index 000000000000..db709b476c5d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.keyguard.shared.model + +/** Model device wakefulness states. */ +enum class BiometricUnlockModel { + /** Mode in which we don't need to wake up the device when we authenticate. */ + NONE, + /** + * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire a + * fingerprint while the screen is off and the device was sleeping. + */ + WAKE_AND_UNLOCK, + /** + * Mode in which we wake the device up, and fade out the Keyguard contents because they were + * already visible while pulsing in doze mode. + */ + WAKE_AND_UNLOCK_PULSING, + /** + * Mode in which we wake up the device, but play the normal dismiss animation. Active when we + * acquire a fingerprint pulsing in doze mode. + */ + SHOW_BOUNCER, + /** + * Mode in which we only wake up the device, and keyguard was not showing when we authenticated. + */ + ONLY_WAKE, + /** + * Mode in which fingerprint unlocks the device or passive auth (ie face auth) unlocks the + * device while being requested when keyguard is occluded or showing. + */ + UNLOCK_COLLAPSING, + /** When bouncer is visible and will be dismissed. */ + DISMISS_BOUNCER, + /** Mode in which fingerprint wakes and unlocks the device from a dream. */ + WAKE_AND_UNLOCK_FROM_DREAM, +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt index 0ca358210813..732a6f7b887a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt @@ -21,10 +21,11 @@ data class TransitionStep( val to: KeyguardState = KeyguardState.NONE, val value: Float = 0f, // constrained [0.0, 1.0] val transitionState: TransitionState = TransitionState.FINISHED, + val ownerName: String = "", ) { constructor( info: TransitionInfo, value: Float, transitionState: TransitionState, - ) : this(info.from, info.to, value, transitionState) + ) : this(info.from, info.to, value, transitionState, info.ownerName) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index a2798f47be65..ff9b736c3d78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -70,8 +70,10 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashSet; import java.util.Map; import java.util.Optional; +import java.util.Set; import javax.inject.Inject; @@ -177,7 +179,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private PendingAuthenticated mPendingAuthenticated = null; private boolean mHasScreenTurnedOnSinceAuthenticating; private boolean mFadedAwayAfterWakeAndUnlock; - private BiometricModeListener mBiometricModeListener; + private Set<BiometricModeListener> mBiometricModeListeners = new HashSet<>(); private final MetricsLogger mMetricsLogger; private final AuthController mAuthController; @@ -326,9 +328,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mKeyguardViewController = keyguardViewController; } - /** Sets a {@link BiometricModeListener}. */ - public void setBiometricModeListener(BiometricModeListener biometricModeListener) { - mBiometricModeListener = biometricModeListener; + /** Adds a {@link BiometricModeListener}. */ + public void addBiometricModeListener(BiometricModeListener listener) { + mBiometricModeListeners.add(listener); + } + + /** Removes a {@link BiometricModeListener}. */ + public void removeBiometricModeListener(BiometricModeListener listener) { + mBiometricModeListeners.remove(listener); } private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() { @@ -511,15 +518,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp break; } onModeChanged(mMode); - if (mBiometricModeListener != null) { - mBiometricModeListener.notifyBiometricAuthModeChanged(); - } Trace.endSection(); } private void onModeChanged(@WakeAndUnlockMode int mode) { - if (mBiometricModeListener != null) { - mBiometricModeListener.onModeChanged(mode); + for (BiometricModeListener listener : mBiometricModeListeners) { + listener.onModeChanged(mode); } } @@ -734,9 +738,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mMode = MODE_NONE; mBiometricType = null; mNotificationShadeWindowController.setForceDozeBrightness(false); - if (mBiometricModeListener != null) { - mBiometricModeListener.onResetMode(); - mBiometricModeListener.notifyBiometricAuthModeChanged(); + for (BiometricModeListener listener : mBiometricModeListeners) { + listener.onResetMode(); } mNumConsecutiveFpFailures = 0; mLastFpFailureUptimeMillis = 0; @@ -845,10 +848,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp /** An interface to interact with the {@link BiometricUnlockController}. */ public interface BiometricModeListener { /** Called when {@code mMode} is reset to {@link #MODE_NONE}. */ - void onResetMode(); + default void onResetMode() {} /** Called when {@code mMode} has changed in {@link #startWakeAndUnlock(int)}. */ - void onModeChanged(@WakeAndUnlockMode int mode); - /** Called after processing {@link #onModeChanged(int)}. */ - void notifyBiometricAuthModeChanged(); + default void onModeChanged(@WakeAndUnlockMode int mode) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 47a12a8b57a3..b3131d07523d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1531,11 +1531,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected void startKeyguard() { Trace.beginSection("CentralSurfaces#startKeyguard"); mBiometricUnlockController = mBiometricUnlockControllerLazy.get(); - mBiometricUnlockController.setBiometricModeListener( + mBiometricUnlockController.addBiometricModeListener( new BiometricUnlockController.BiometricModeListener() { @Override public void onResetMode() { setWakeAndUnlocking(false); + notifyBiometricAuthModeChanged(); } @Override @@ -1546,11 +1547,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { case BiometricUnlockController.MODE_WAKE_AND_UNLOCK: setWakeAndUnlocking(true); } - } - - @Override - public void notifyBiometricAuthModeChanged() { - CentralSurfacesImpl.this.notifyBiometricAuthModeChanged(); + notifyBiometricAuthModeChanged(); } private void setWakeAndUnlocking(boolean wakeAndUnlocking) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index b9ab9d37a805..53d9b87b2346 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -21,8 +21,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Position import com.android.systemui.doze.DozeHost import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.whenever @@ -46,6 +48,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var dozeHost: DozeHost @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle + @Mock private lateinit var biometricUnlockController: BiometricUnlockController private lateinit var underTest: KeyguardRepositoryImpl @@ -59,6 +62,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { keyguardStateController, dozeHost, wakefulnessLifecycle, + biometricUnlockController, ) } @@ -190,7 +194,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test - fun wakefullness() = runBlockingTest { + fun wakefulness() = runBlockingTest { val values = mutableListOf<WakefulnessModel>() val job = underTest.wakefulnessState.onEach(values::add).launchIn(this) @@ -217,4 +221,63 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { job.cancel() verify(wakefulnessLifecycle).removeObserver(captor.value) } + + @Test + fun isBouncerShowing() = runBlockingTest { + whenever(keyguardStateController.isBouncerShowing).thenReturn(false) + var latest: Boolean? = null + val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this) + + assertThat(latest).isFalse() + + val captor = argumentCaptor<KeyguardStateController.Callback>() + verify(keyguardStateController).addCallback(captor.capture()) + + whenever(keyguardStateController.isBouncerShowing).thenReturn(true) + captor.value.onBouncerShowingChanged() + assertThat(latest).isTrue() + + whenever(keyguardStateController.isBouncerShowing).thenReturn(false) + captor.value.onBouncerShowingChanged() + assertThat(latest).isFalse() + + job.cancel() + } + + @Test + fun biometricUnlockState() = runBlockingTest { + val values = mutableListOf<BiometricUnlockModel>() + val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this) + + val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>() + verify(biometricUnlockController).addBiometricModeListener(captor.capture()) + + captor.value.onModeChanged(BiometricUnlockController.MODE_NONE) + captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK) + captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) + captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER) + captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE) + captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING) + captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER) + captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM) + + assertThat(values) + .isEqualTo( + listOf( + // Initial value will be NONE, followed by onModeChanged() call + BiometricUnlockModel.NONE, + BiometricUnlockModel.NONE, + BiometricUnlockModel.WAKE_AND_UNLOCK, + BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING, + BiometricUnlockModel.SHOW_BOUNCER, + BiometricUnlockModel.ONLY_WAKE, + BiometricUnlockModel.UNLOCK_COLLAPSING, + BiometricUnlockModel.DISMISS_BOUNCER, + BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM, + ) + ) + + job.cancel() + verify(biometricUnlockController).removeBiometricModeListener(captor.value) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index 64913c7c28c2..27d5d0a98978 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -128,11 +128,15 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { assertThat(steps.size).isEqualTo(3) assertThat(steps[0]) - .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED)) + .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME)) assertThat(steps[1]) - .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING)) + .isEqualTo( + TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING, OWNER_NAME) + ) assertThat(steps[2]) - .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED)) + .isEqualTo( + TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME) + ) job.cancel() } @@ -174,15 +178,22 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { } private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) { - assertThat(steps[0]).isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED)) + assertThat(steps[0]) + .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME)) fractions.forEachIndexed { index, fraction -> assertThat(steps[index + 1]) .isEqualTo( - TransitionStep(AOD, LOCKSCREEN, fraction.toFloat(), TransitionState.RUNNING) + TransitionStep( + AOD, + LOCKSCREEN, + fraction.toFloat(), + TransitionState.RUNNING, + OWNER_NAME + ) ) } assertThat(steps[steps.size - 1]) - .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED)) + .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME)) assertThat(wtfHandler.failed).isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index 0424c28a1c24..6333b244fd38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -54,9 +54,11 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { @Test fun `transition collectors receives only appropriate events`() = runBlocking(IMMEDIATE) { - var goneToAodSteps = mutableListOf<TransitionStep>() + var lockscreenToAodSteps = mutableListOf<TransitionStep>() val job1 = - underTest.goneToAodTransition.onEach { goneToAodSteps.add(it) }.launchIn(this) + underTest.lockscreenToAodTransition + .onEach { lockscreenToAodSteps.add(it) } + .launchIn(this) var aodToLockscreenSteps = mutableListOf<TransitionStep>() val job2 = @@ -70,14 +72,14 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)) steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING)) steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED)) - steps.add(TransitionStep(GONE, AOD, 0f, STARTED)) - steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING)) - steps.add(TransitionStep(GONE, AOD, 0.2f, RUNNING)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING)) steps.forEach { repository.sendTransitionStep(it) } assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5)) - assertThat(goneToAodSteps).isEqualTo(steps.subList(5, 8)) + assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8)) job1.cancel() job2.cancel() @@ -119,10 +121,8 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { fun keyguardStateTests() = runBlocking(IMMEDIATE) { var finishedSteps = mutableListOf<KeyguardState>() - val job1 = + val job = underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this) - var startedSteps = mutableListOf<KeyguardState>() - val job2 = underTest.startedKeyguardState.onEach { startedSteps.add(it) }.launchIn(this) val steps = mutableListOf<TransitionStep>() @@ -137,10 +137,60 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { steps.forEach { repository.sendTransitionStep(it) } assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD)) - assertThat(startedSteps).isEqualTo(listOf(LOCKSCREEN, AOD, GONE)) - job1.cancel() - job2.cancel() + job.cancel() + } + + @Test + fun finishedKeyguardTransitionStepTests() = + runBlocking(IMMEDIATE) { + var finishedSteps = mutableListOf<TransitionStep>() + val job = + underTest.finishedKeyguardTransitionStep + .onEach { finishedSteps.add(it) } + .launchIn(this) + + val steps = mutableListOf<TransitionStep>() + + steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)) + steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING)) + steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) + steps.add(TransitionStep(AOD, GONE, 1f, STARTED)) + + steps.forEach { repository.sendTransitionStep(it) } + + assertThat(finishedSteps).isEqualTo(listOf(steps[2], steps[5])) + + job.cancel() + } + + @Test + fun startedKeyguardTransitionStepTests() = + runBlocking(IMMEDIATE) { + var startedSteps = mutableListOf<TransitionStep>() + val job = + underTest.startedKeyguardTransitionStep + .onEach { startedSteps.add(it) } + .launchIn(this) + + val steps = mutableListOf<TransitionStep>() + + steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)) + steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING)) + steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING)) + steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) + steps.add(TransitionStep(AOD, GONE, 1f, STARTED)) + + steps.forEach { repository.sendTransitionStep(it) } + + assertThat(startedSteps).isEqualTo(listOf(steps[0], steps[3], steps[6])) + + job.cancel() } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 6fa217415044..b17747ad2d92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -143,7 +143,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController, mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); - mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener); + mBiometricUnlockController.addBiometricModeListener(mBiometricModeListener); } @Test diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 11178db8afd2..627bd096143e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import com.android.systemui.common.shared.model.Position +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel import kotlinx.coroutines.flow.Flow @@ -52,6 +53,12 @@ class FakeKeyguardRepository : KeyguardRepository { private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP) override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState + private val _isBouncerShowing = MutableStateFlow(false) + override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing + + private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE) + override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState + override fun isKeyguardShowing(): Boolean { return _isKeyguardShowing.value } |