diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index d48d7ffcd8243..c9b8712bdde97 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/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index c756a17976bf9..2bb3a5f437f5a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -106,8 +106,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout { static final int USER_TYPE_WORK_PROFILE = 2; static final int USER_TYPE_SECONDARY_USER = 3; - @IntDef({MODE_DEFAULT, MODE_ONE_HANDED, MODE_USER_SWITCHER}) + @IntDef({MODE_UNINITIALIZED, MODE_DEFAULT, MODE_ONE_HANDED, MODE_USER_SWITCHER}) public @interface Mode {} + static final int MODE_UNINITIALIZED = -1; static final int MODE_DEFAULT = 0; static final int MODE_ONE_HANDED = 1; static final int MODE_USER_SWITCHER = 2; @@ -154,7 +155,11 @@ public class KeyguardSecurityContainer extends ConstraintLayout { private boolean mDisappearAnimRunning; private SwipeListener mSwipeListener; private ViewMode mViewMode = new DefaultViewMode(); - private @Mode int mCurrentMode = MODE_DEFAULT; + /* + * Using MODE_UNINITIALIZED to mean the view mode is set to DefaultViewMode, but init() has not + * yet been called on it. This will happen when the ViewController is initialized. + */ + private @Mode int mCurrentMode = MODE_UNINITIALIZED; private int mWidth = -1; private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = @@ -347,6 +352,8 @@ public class KeyguardSecurityContainer extends ConstraintLayout { private String modeToString(@Mode int mode) { switch (mode) { + case MODE_UNINITIALIZED: + return "Uninitialized"; case MODE_DEFAULT: return "Default"; case MODE_ONE_HANDED: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 79a01b9c97179..fbb114c72add9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -318,6 +318,7 @@ public class KeyguardSecurityContainerController extends ViewController + /** Observable for whether the bouncer is showing. */ + val isBouncerShowing: Flow + /** * Observable for whether we are in doze state. * @@ -95,6 +101,9 @@ interface KeyguardRepository { /** Observable for device wake/sleep state */ val wakefulnessState: Flow + /** Observable for biometric unlock modes */ + val biometricUnlockState: Flow + /** * 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 = 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 = conflatedCallbackFlow { val callback = @@ -248,6 +281,24 @@ constructor( awaitClose { wakefulnessLifecycle.removeObserver(callback) } } + override val biometricUnlockState: Flow = 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 0000000000000..7e01db34319f9 --- /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 03c6a789326e4..614ff8d930d82 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 = repository.dozeAmount /** Whether the system is in doze mode. */ val isDozing: Flow = repository.isDozing - /** Whether the keyguard is showing to not. */ + /** Whether the keyguard is showing or not. */ val isKeyguardShowing: Flow = repository.isKeyguardShowing + /** Whether the bouncer is showing or not. */ + val isBouncerShowing: Flow = repository.isBouncerShowing /** The device wake/sleep state */ val wakefulnessState: Flow = repository.wakefulnessState + /** Observable for the [StatusBarState] */ + val statusBarState: Flow = 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 = 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 83d94325b9d49..57fb4a1147003 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 d5ea77b8e729d..a7c6d44503368 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 dffd097a77c5b..749183e241e59 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 = repository.transition(LOCKSCREEN, AOD) - /** GONE->AOD information. */ - val goneToAodTransition: Flow = repository.transition(GONE, AOD) + /** (any)->AOD transition information */ + val anyStateToAodTransition: Flow = + 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 = + repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED } + /* The last completed [KeyguardState] transition */ val finishedKeyguardState: Flow = - 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 = - repository.transitions - .filter { step -> step.transitionState == TransitionState.STARTED } - .map { step -> step.to } + /* The last [TransitionStep] with a [TransitionState] of STARTED */ + val startedKeyguardTransitionStep: Flow = + 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 761f3fd9d9f95..fd4814d2bc944 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 728bafae2a4c2..37f33afbf53e4 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 0000000000000..db709b476c5d3 --- /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 0ca358210813d..732a6f7b887a5 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 87b0423f1b744..6e68079706f48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -67,8 +67,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; @@ -167,7 +169,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private PendingAuthenticated mPendingAuthenticated = null; private boolean mHasScreenTurnedOnSinceAuthenticating; private boolean mFadedAwayAfterWakeAndUnlock; - private BiometricModeListener mBiometricModeListener; + private Set mBiometricModeListeners = new HashSet<>(); private final MetricsLogger mMetricsLogger; private final AuthController mAuthController; @@ -314,9 +316,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() { @@ -499,15 +506,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); } } @@ -714,9 +718,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; @@ -825,10 +828,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 9df014cd02237..c6aab760299b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1527,11 +1527,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 @@ -1542,11 +1543,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/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 52f8ef8b7ebcd..0a2b3d8498c44 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -193,6 +193,15 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mSecurityCallback); } + @Test + public void onInitConfiguresViewMode() { + mKeyguardSecurityContainerController.onInit(); + verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), + eq(mUserSwitcherController), + any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), + eq(mFalsingA11yDelegate)); + } + @Test public void showSecurityScreen_canInflateAllModes() { SecurityMode[] modes = SecurityMode.values(); 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 b9ab9d37a805f..53d9b87b23465 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() 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() + 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() + val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this) + + val captor = argumentCaptor() + 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 64913c7c28c21..27d5d0a989788 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, fractions: List) { - 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 0424c28a1c248..6333b244fd38c 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() + var lockscreenToAodSteps = mutableListOf() val job1 = - underTest.goneToAodTransition.onEach { goneToAodSteps.add(it) }.launchIn(this) + underTest.lockscreenToAodTransition + .onEach { lockscreenToAodSteps.add(it) } + .launchIn(this) var aodToLockscreenSteps = mutableListOf() 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() - val job1 = + val job = underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this) - var startedSteps = mutableListOf() - val job2 = underTest.startedKeyguardState.onEach { startedSteps.add(it) }.launchIn(this) val steps = mutableListOf() @@ -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() + val job = + underTest.finishedKeyguardTransitionStep + .onEach { finishedSteps.add(it) } + .launchIn(this) + + val steps = mutableListOf() + + 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() + val job = + underTest.startedKeyguardTransitionStep + .onEach { startedSteps.add(it) } + .launchIn(this) + + val steps = mutableListOf() + + 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 6fa2174150449..b17747ad2d92e 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 11178db8afd2a..627bd096143e3 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 = _wakefulnessState + private val _isBouncerShowing = MutableStateFlow(false) + override val isBouncerShowing: Flow = _isBouncerShowing + + private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE) + override val biometricUnlockState: Flow = _biometricUnlockState + override fun isKeyguardShowing(): Boolean { return _isKeyguardShowing.value }