diff options
| author | 2022-12-22 13:09:22 +0000 | |
|---|---|---|
| committer | 2022-12-22 13:09:22 +0000 | |
| commit | 766d1ecd56f57aaeb7923e2c64f4fa911cb35b04 (patch) | |
| tree | 68c721f72ec023abf55c7985630456d6277c709b | |
| parent | 35980bdb3ebde39a307a70f60d6cccd6ff11cacd (diff) | |
| parent | 49889126c437ef5817b941547856ef1573c6f763 (diff) | |
Merge "Transitions - Add occlusion state" into tm-qpr-dev am: 49889126c4
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20787426
Change-Id: I96a27ea8a60157fd558529714cdd28ffcc50f90d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
25 files changed, 621 insertions, 233 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index abe9355d3375..c882f8adceb6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -101,13 +101,11 @@ constructor( transitionViewModel.dreamOverlayTranslationY(it.translationYPx) } .collect { px -> - setElementsTranslationYAtPosition( - px, - ComplicationLayoutParams.POSITION_TOP - ) - setElementsTranslationYAtPosition( - px, - ComplicationLayoutParams.POSITION_BOTTOM + ComplicationLayoutParams.iteratePositions( + { position: Int -> + setElementsTranslationYAtPosition(px, position) + }, + POSITION_TOP or POSITION_BOTTOM ) } } @@ -115,15 +113,15 @@ constructor( /* Alpha animations, when moving from DREAMING->LOCKSCREEN state */ launch { transitionViewModel.dreamOverlayAlpha.collect { alpha -> - setElementsAlphaAtPosition( - alpha = alpha, - position = ComplicationLayoutParams.POSITION_TOP, - fadingOut = true, - ) - setElementsAlphaAtPosition( - alpha = alpha, - position = ComplicationLayoutParams.POSITION_BOTTOM, - fadingOut = true, + ComplicationLayoutParams.iteratePositions( + { position: Int -> + setElementsAlphaAtPosition( + alpha = alpha, + position = position, + fadingOut = true, + ) + }, + POSITION_TOP or POSITION_BOTTOM ) } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamCallbackController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt index ab4632b08fa1..d5ff8f21abb2 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamCallbackController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt @@ -20,26 +20,39 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.policy.CallbackController import javax.inject.Inject -/** Dream-related callback information */ +/** Dream overlay-related callback information */ @SysUISingleton -class DreamCallbackController @Inject constructor() : - CallbackController<DreamCallbackController.DreamCallback> { +class DreamOverlayCallbackController @Inject constructor() : + CallbackController<DreamOverlayCallbackController.Callback> { - private val callbacks = mutableSetOf<DreamCallbackController.DreamCallback>() + private val callbacks = mutableSetOf<DreamOverlayCallbackController.Callback>() - override fun addCallback(callback: DreamCallbackController.DreamCallback) { + var isDreaming = false + private set + + override fun addCallback(callback: DreamOverlayCallbackController.Callback) { callbacks.add(callback) } - override fun removeCallback(callback: DreamCallbackController.DreamCallback) { + override fun removeCallback(callback: DreamOverlayCallbackController.Callback) { callbacks.remove(callback) } fun onWakeUp() { + isDreaming = false callbacks.forEach { it.onWakeUp() } } - interface DreamCallback { + fun onStartDream() { + isDreaming = true + callbacks.forEach { it.onStartDream() } + } + + interface Callback { + /** Dream overlay has ended */ fun onWakeUp() + + /** Dream overlay has started */ + fun onStartDream() } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 1be9cd198604..eedef65a8e4d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -64,7 +64,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // A controller for the dream overlay container view (which contains both the status bar and the // content area). private DreamOverlayContainerViewController mDreamOverlayContainerViewController; - private final DreamCallbackController mDreamCallbackController; + private final DreamOverlayCallbackController mDreamOverlayCallbackController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Nullable private final ComponentName mLowLightDreamComponent; @@ -134,7 +134,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ UiEventLogger uiEventLogger, @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT) ComponentName lowLightDreamComponent, - DreamCallbackController dreamCallbackController) { + DreamOverlayCallbackController dreamOverlayCallbackController) { mContext = context; mExecutor = executor; mWindowManager = windowManager; @@ -143,7 +143,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mStateController = stateController; mUiEventLogger = uiEventLogger; - mDreamCallbackController = dreamCallbackController; + mDreamOverlayCallbackController = dreamOverlayCallbackController; final ViewModelStore viewModelStore = new ViewModelStore(); final Complication.Host host = @@ -203,6 +203,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); + mDreamOverlayCallbackController.onStartDream(); mStarted = true; }); } @@ -219,7 +220,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onWakeUp(@NonNull Runnable onCompletedCallback) { mExecutor.execute(() -> { if (mDreamOverlayContainerViewController != null) { - mDreamCallbackController.onWakeUp(); + mDreamOverlayCallbackController.onWakeUp(); mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor); } }); 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 9a0fbbf60f60..a4fd087a24b1 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 @@ -29,8 +29,7 @@ import com.android.systemui.doze.DozeHost import com.android.systemui.doze.DozeMachine import com.android.systemui.doze.DozeTransitionCallback import com.android.systemui.doze.DozeTransitionListener -import com.android.systemui.dreams.DreamCallbackController -import com.android.systemui.dreams.DreamCallbackController.DreamCallback +import com.android.systemui.dreams.DreamOverlayCallbackController import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource @@ -49,7 +48,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.merge /** Defines interface for classes that encapsulate application state for the keyguard. */ interface KeyguardRepository { @@ -81,6 +79,9 @@ interface KeyguardRepository { */ val isKeyguardShowing: Flow<Boolean> + /** Is an activity showing over the keyguard? */ + val isKeyguardOccluded: Flow<Boolean> + /** Observable for the signal that keyguard is about to go away. */ val isKeyguardGoingAway: Flow<Boolean> @@ -107,6 +108,9 @@ interface KeyguardRepository { */ val isDreaming: Flow<Boolean> + /** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */ + val isDreamingWithOverlay: Flow<Boolean> + /** * Observable for the amount of doze we are currently in. * @@ -179,7 +183,7 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val dozeTransitionListener: DozeTransitionListener, private val authController: AuthController, - private val dreamCallbackController: DreamCallbackController, + private val dreamOverlayCallbackController: DreamOverlayCallbackController, ) : KeyguardRepository { private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) override val animateBottomAreaDozingTransitions = @@ -191,28 +195,55 @@ constructor( private val _clockPosition = MutableStateFlow(Position(0, 0)) override val clockPosition = _clockPosition.asStateFlow() - override val isKeyguardShowing: Flow<Boolean> = conflatedCallbackFlow { - val callback = - object : KeyguardStateController.Callback { - override fun onKeyguardShowingChanged() { - trySendWithFailureLogging( - keyguardStateController.isShowing, - TAG, - "updated isKeyguardShowing" - ) - } + override val isKeyguardShowing: Flow<Boolean> = + conflatedCallbackFlow { + val callback = + object : KeyguardStateController.Callback { + override fun onKeyguardShowingChanged() { + trySendWithFailureLogging( + keyguardStateController.isShowing, + TAG, + "updated isKeyguardShowing" + ) + } + } + + keyguardStateController.addCallback(callback) + // Adding the callback does not send an initial update. + trySendWithFailureLogging( + keyguardStateController.isShowing, + TAG, + "initial isKeyguardShowing" + ) + + awaitClose { keyguardStateController.removeCallback(callback) } } + .distinctUntilChanged() - keyguardStateController.addCallback(callback) - // Adding the callback does not send an initial update. - trySendWithFailureLogging( - keyguardStateController.isShowing, - TAG, - "initial isKeyguardShowing" - ) + override val isKeyguardOccluded: Flow<Boolean> = + conflatedCallbackFlow { + val callback = + object : KeyguardStateController.Callback { + override fun onKeyguardShowingChanged() { + trySendWithFailureLogging( + keyguardStateController.isOccluded, + TAG, + "updated isKeyguardOccluded" + ) + } + } - awaitClose { keyguardStateController.removeCallback(callback) } - } + keyguardStateController.addCallback(callback) + // Adding the callback does not send an initial update. + trySendWithFailureLogging( + keyguardStateController.isOccluded, + TAG, + "initial isKeyguardOccluded" + ) + + awaitClose { keyguardStateController.removeCallback(callback) } + } + .distinctUntilChanged() override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { val callback = @@ -279,36 +310,45 @@ constructor( } .distinctUntilChanged() - override val isDreaming: Flow<Boolean> = - merge( - conflatedCallbackFlow { - val callback = - object : KeyguardUpdateMonitorCallback() { - override fun onDreamingStateChanged(isDreaming: Boolean) { - trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming") - } + override val isDreamingWithOverlay: Flow<Boolean> = + conflatedCallbackFlow { + val callback = + object : DreamOverlayCallbackController.Callback { + override fun onStartDream() { + trySendWithFailureLogging(true, TAG, "updated isDreamingWithOverlay") } - keyguardUpdateMonitor.registerCallback(callback) - trySendWithFailureLogging( - keyguardUpdateMonitor.isDreaming, - TAG, - "initial isDreaming", - ) + override fun onWakeUp() { + trySendWithFailureLogging(false, TAG, "updated isDreamingWithOverlay") + } + } + dreamOverlayCallbackController.addCallback(callback) + trySendWithFailureLogging( + dreamOverlayCallbackController.isDreaming, + TAG, + "initial isDreamingWithOverlay", + ) + + awaitClose { dreamOverlayCallbackController.removeCallback(callback) } + } + .distinctUntilChanged() - awaitClose { keyguardUpdateMonitor.removeCallback(callback) } - }, - conflatedCallbackFlow { - val callback = - object : DreamCallback { - override fun onWakeUp() { - trySendWithFailureLogging(false, TAG, "updated isDreaming") - } + override val isDreaming: Flow<Boolean> = + conflatedCallbackFlow { + val callback = + object : KeyguardUpdateMonitorCallback() { + override fun onDreamingStateChanged(isDreaming: Boolean) { + trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming") } - dreamCallbackController.addCallback(callback) + } + keyguardUpdateMonitor.registerCallback(callback) + trySendWithFailureLogging( + keyguardUpdateMonitor.isDreaming, + TAG, + "initial isDreaming", + ) - awaitClose { dreamCallbackController.removeCallback(callback) } - } - ) + awaitClose { keyguardUpdateMonitor.removeCallback(callback) } + } .distinctUntilChanged() override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index d72d7183b0f0..343c2dc172fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -131,6 +131,10 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio } override fun startTransition(info: TransitionInfo): UUID? { + if (lastStep.from == info.from && lastStep.to == info.to) { + Log.i(TAG, "Duplicate call to start the transition, rejecting: $info") + return null + } if (lastStep.transitionState != TransitionState.FINISHED) { Log.i(TAG, "Transition still active: $lastStep, canceling") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index f3d2905121bb..c2d139c21074 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -21,6 +21,7 @@ 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.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo @@ -30,33 +31,33 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @SysUISingleton -class AodLockscreenTransitionInteractor +class FromAodTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, -) : TransitionInteractor(AodLockscreenTransitionInteractor::class.simpleName!!) { +) : TransitionInteractor(FromAodTransitionInteractor::class.simpleName!!) { override fun start() { - listenForTransitionToAodFromLockscreen() - listenForTransitionToLockscreenFromDozeStates() + listenForAodToLockscreen() + listenForAodToGone() } - private fun listenForTransitionToAodFromLockscreen() { + private fun listenForAodToLockscreen() { scope.launch { keyguardInteractor - .dozeTransitionTo(DozeStateModel.DOZE_AOD) + .dozeTransitionTo(DozeStateModel.FINISH) .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (dozeToAod, lastStartedStep) = pair - if (lastStartedStep.to == KeyguardState.LOCKSCREEN) { + if (lastStartedStep.to == KeyguardState.AOD) { keyguardTransitionRepository.startTransition( TransitionInfo( name, - KeyguardState.LOCKSCREEN, KeyguardState.AOD, + KeyguardState.LOCKSCREEN, getAnimator(), ) ) @@ -65,20 +66,20 @@ constructor( } } - private fun listenForTransitionToLockscreenFromDozeStates() { - val canGoToLockscreen = setOf(KeyguardState.AOD, KeyguardState.DOZING) + private fun listenForAodToGone() { scope.launch { - keyguardInteractor - .dozeTransitionTo(DozeStateModel.FINISH) - .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + keyguardInteractor.biometricUnlockState + .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair) .collect { pair -> - val (dozeToAod, lastStartedStep) = pair - if (canGoToLockscreen.contains(lastStartedStep.to)) { + val (biometricUnlockState, keyguardState) = pair + if ( + keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState) + ) { keyguardTransitionRepository.startTransition( TransitionInfo( name, - lastStartedStep.to, - KeyguardState.LOCKSCREEN, + KeyguardState.AOD, + KeyguardState.GONE, getAnimator(), ) ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt index 056c44dc72cf..0e9c44703205 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromBouncerTransitionInteractor.kt @@ -23,16 +23,18 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.sample import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @SysUISingleton -class BouncerToGoneTransitionInteractor +class FromBouncerTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, @@ -40,15 +42,54 @@ constructor( private val shadeRepository: ShadeRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor -) : TransitionInteractor(BouncerToGoneTransitionInteractor::class.simpleName!!) { +) : TransitionInteractor(FromBouncerTransitionInteractor::class.simpleName!!) { private var transitionId: UUID? = null override fun start() { - listenForKeyguardGoingAway() + listenForBouncerToGone() + listenForBouncerToLockscreenOrAod() } - private fun listenForKeyguardGoingAway() { + private fun listenForBouncerToLockscreenOrAod() { + scope.launch { + keyguardInteractor.isBouncerShowing + .sample( + combine( + keyguardInteractor.wakefulnessModel, + keyguardTransitionInteractor.startedKeyguardTransitionStep, + ::Pair + ), + ::toTriple + ) + .collect { triple -> + val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple + if ( + !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER + ) { + val to = + if ( + wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP || + wakefulnessState.state == WakefulnessState.ASLEEP + ) { + KeyguardState.AOD + } else { + KeyguardState.LOCKSCREEN + } + keyguardTransitionRepository.startTransition( + TransitionInfo( + ownerName = name, + from = KeyguardState.BOUNCER, + to = to, + animator = getAnimator(), + ) + ) + } + } + } + } + + private fun listenForBouncerToGone() { scope.launch { keyguardInteractor.isKeyguardGoingAway .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 95d96025cf4a..fd2d271e40f9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -21,36 +21,46 @@ 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.DozeStateModel.Companion.isDozeOff 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 kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @SysUISingleton -class LockscreenGoneTransitionInteractor +class FromDozingTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, - private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, -) : TransitionInteractor(LockscreenGoneTransitionInteractor::class.simpleName!!) { + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, +) : TransitionInteractor(FromDozingTransitionInteractor::class.simpleName!!) { override fun start() { + listenForDozingToLockscreen() + } + + private fun listenForDozingToLockscreen() { scope.launch { - keyguardInteractor.isKeyguardGoingAway + keyguardInteractor.dozeTransitionModel .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> - val (isKeyguardGoingAway, lastStartedStep) = pair - if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { + val (dozeTransitionModel, lastStartedTransition) = pair + if ( + isDozeOff(dozeTransitionModel.to) && + lastStartedTransition.to == KeyguardState.DOZING + ) { keyguardTransitionRepository.startTransition( TransitionInfo( name, + KeyguardState.DOZING, KeyguardState.LOCKSCREEN, - KeyguardState.GONE, getAnimator(), ) ) @@ -59,14 +69,14 @@ constructor( } } - private fun getAnimator(): ValueAnimator { + private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) - setDuration(TRANSITION_DURATION_MS) + setDuration(duration.inWholeMilliseconds) } } companion object { - private const val TRANSITION_DURATION_MS = 10L + private val DEFAULT_DURATION = 500.milliseconds } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 188930c36097..3b09ae7ba8ea 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -36,52 +36,48 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @SysUISingleton -class DreamingTransitionInteractor +class FromDreamingTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, -) : TransitionInteractor(DreamingTransitionInteractor::class.simpleName!!) { - - private val canDreamFrom = - setOf(KeyguardState.LOCKSCREEN, KeyguardState.GONE, KeyguardState.DOZING) +) : TransitionInteractor(FromDreamingTransitionInteractor::class.simpleName!!) { override fun start() { - listenForEntryToDreaming() listenForDreamingToLockscreen() + listenForDreamingToOccluded() listenForDreamingToGone() listenForDreamingToDozing() } - private fun listenForEntryToDreaming() { + private fun listenForDreamingToLockscreen() { scope.launch { - keyguardInteractor.isDreaming + // Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which + // otherwise would have gone through OCCLUDED first + keyguardInteractor.isDreamingWithOverlay .sample( combine( keyguardInteractor.dozeTransitionModel, - keyguardTransitionInteractor.finishedKeyguardState, + keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair ), ::toTriple ) .collect { triple -> - val (isDreaming, dozeTransitionModel, keyguardState) = triple - // Dozing/AOD and dreaming have overlapping events. If the state remains in - // FINISH, it means that doze mode is not running and DREAMING is ok to - // commence. + val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple if ( - isDozeOff(dozeTransitionModel.to) && - isDreaming && - canDreamFrom.contains(keyguardState) + !isDreaming && + isDozeOff(dozeTransitionModel.to) && + lastStartedTransition.to == KeyguardState.DREAMING ) { keyguardTransitionRepository.startTransition( TransitionInfo( name, - keyguardState, KeyguardState.DREAMING, - getAnimator(), + KeyguardState.LOCKSCREEN, + getAnimator(TO_LOCKSCREEN_DURATION), ) ) } @@ -89,30 +85,35 @@ constructor( } } - private fun listenForDreamingToLockscreen() { + private fun listenForDreamingToOccluded() { scope.launch { keyguardInteractor.isDreaming .sample( combine( - keyguardInteractor.dozeTransitionModel, + keyguardInteractor.isKeyguardOccluded, keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair, ), ::toTriple ) .collect { triple -> - val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple + val (isDreaming, isOccluded, lastStartedTransition) = triple if ( - isDozeOff(dozeTransitionModel.to) && + isOccluded && !isDreaming && - lastStartedTransition.to == KeyguardState.DREAMING + (lastStartedTransition.to == KeyguardState.DREAMING || + lastStartedTransition.to == KeyguardState.LOCKSCREEN) ) { + // At the moment, checking for LOCKSCREEN state above provides a corrective + // action. There's no great signal to determine when the dream is ending + // and a transition to OCCLUDED is beginning directly. For now, the solution + // is DREAMING->LOCKSCREEN->OCCLUDED keyguardTransitionRepository.startTransition( TransitionInfo( name, - KeyguardState.DREAMING, - KeyguardState.LOCKSCREEN, - getAnimator(TO_LOCKSCREEN_DURATION), + lastStartedTransition.to, + KeyguardState.OCCLUDED, + getAnimator(), ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index a50e75909dd8..553fafeb92c3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -30,19 +30,44 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @SysUISingleton -class GoneAodTransitionInteractor +class FromGoneTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, -) : TransitionInteractor(GoneAodTransitionInteractor::class.simpleName!!) { +) : TransitionInteractor(FromGoneTransitionInteractor::class.simpleName!!) { override fun start() { + listenForGoneToAod() + listenForGoneToDreaming() + } + + private fun listenForGoneToDreaming() { + scope.launch { + keyguardInteractor.isAbleToDream + .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair) + .collect { pair -> + val (isAbleToDream, keyguardState) = pair + if (isAbleToDream && keyguardState == KeyguardState.GONE) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + KeyguardState.GONE, + KeyguardState.DREAMING, + getAnimator(), + ) + ) + } + } + } + } + + private fun listenForGoneToAod() { scope.launch { keyguardInteractor.wakefulnessModel - .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) + .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair) .collect { pair -> val (wakefulnessState, keyguardState) = pair if ( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 5cb7d709a1a2..326acc9eb762 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -21,11 +21,11 @@ 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.DozeStateModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState -import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.sample import java.util.UUID @@ -36,57 +36,54 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @SysUISingleton -class LockscreenBouncerTransitionInteractor +class FromLockscreenTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, private val shadeRepository: ShadeRepository, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, - private val keyguardTransitionInteractor: KeyguardTransitionInteractor -) : TransitionInteractor(LockscreenBouncerTransitionInteractor::class.simpleName!!) { +) : TransitionInteractor(FromLockscreenTransitionInteractor::class.simpleName!!) { private var transitionId: UUID? = null override fun start() { - listenForDraggingUpToBouncer() - listenForBouncer() + listenForLockscreenToGone() + listenForLockscreenToOccluded() + listenForLockscreenToAod() + listenForLockscreenToBouncer() + listenForLockscreenToDreaming() + listenForLockscreenToBouncerDragging() } - private fun listenForBouncer() { + private fun listenForLockscreenToDreaming() { scope.launch { - keyguardInteractor.isBouncerShowing - .sample( - combine( - keyguardInteractor.wakefulnessModel, - keyguardTransitionInteractor.startedKeyguardTransitionStep, - ::Pair - ), - ::toTriple - ) - .collect { triple -> - val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple - if ( - !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER - ) { - val to = - if ( - wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP || - wakefulnessState.state == WakefulnessState.ASLEEP - ) { - KeyguardState.AOD - } else { - KeyguardState.LOCKSCREEN - } + keyguardInteractor.isAbleToDream + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { pair -> + val (isAbleToDream, lastStartedTransition) = pair + if (isAbleToDream && lastStartedTransition.to == KeyguardState.LOCKSCREEN) { keyguardTransitionRepository.startTransition( TransitionInfo( - ownerName = name, - from = KeyguardState.BOUNCER, - to = to, - animator = getAnimator(), + name, + KeyguardState.LOCKSCREEN, + KeyguardState.DREAMING, + getAnimator(), ) ) - } else if ( + } + } + } + } + + private fun listenForLockscreenToBouncer() { + scope.launch { + keyguardInteractor.isBouncerShowing + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { pair -> + val (isBouncerShowing, lastStartedTransitionStep) = pair + if ( isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN ) { keyguardTransitionRepository.startTransition( @@ -98,13 +95,12 @@ constructor( ) ) } - Unit } } } /* Starts transitions when manually dragging up the bouncer from the lockscreen. */ - private fun listenForDraggingUpToBouncer() { + private fun listenForLockscreenToBouncerDragging() { scope.launch { shadeRepository.shadeModel .sample( @@ -157,6 +153,76 @@ constructor( } } + private fun listenForLockscreenToGone() { + scope.launch { + keyguardInteractor.isKeyguardGoingAway + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { pair -> + val (isKeyguardGoingAway, lastStartedStep) = pair + if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + KeyguardState.LOCKSCREEN, + KeyguardState.GONE, + getAnimator(), + ) + ) + } + } + } + } + + private fun listenForLockscreenToOccluded() { + scope.launch { + keyguardInteractor.isKeyguardOccluded + .sample( + combine( + keyguardTransitionInteractor.finishedKeyguardState, + keyguardInteractor.isDreaming, + ::Pair + ), + ::toTriple + ) + .collect { triple -> + val (isOccluded, keyguardState, isDreaming) = triple + // Occlusion signals come from the framework, and should interrupt any + // existing transition + if (isOccluded && !isDreaming) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + keyguardState, + KeyguardState.OCCLUDED, + getAnimator(), + ) + ) + } + } + } + } + + private fun listenForLockscreenToAod() { + scope.launch { + keyguardInteractor + .dozeTransitionTo(DozeStateModel.DOZE_AOD) + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { pair -> + val (dozeToAod, lastStartedStep) = pair + if (lastStartedStep.to == KeyguardState.LOCKSCREEN) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + KeyguardState.LOCKSCREEN, + KeyguardState.AOD, + getAnimator(), + ) + ) + } + } + } + } + private fun getAnimator(): ValueAnimator { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) @@ -165,6 +231,6 @@ constructor( } companion object { - private const val TRANSITION_DURATION_MS = 300L + private const val TRANSITION_DURATION_MS = 500L } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index dad166f2b5e0..937e8ffd9f61 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -21,39 +21,65 @@ 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.Companion.isWakeAndUnlock 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 kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @SysUISingleton -class AodToGoneTransitionInteractor +class FromOccludedTransitionInteractor @Inject constructor( @Application private val scope: CoroutineScope, private val keyguardInteractor: KeyguardInteractor, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, -) : TransitionInteractor(AodToGoneTransitionInteractor::class.simpleName!!) { +) : TransitionInteractor(FromOccludedTransitionInteractor::class.simpleName!!) { override fun start() { + listenForOccludedToLockscreen() + listenForOccludedToDreaming() + } + + private fun listenForOccludedToDreaming() { + scope.launch { + keyguardInteractor.isAbleToDream + .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair) + .collect { pair -> + val (isAbleToDream, keyguardState) = pair + if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + KeyguardState.OCCLUDED, + KeyguardState.DREAMING, + getAnimator(), + ) + ) + } + } + } + } + + private fun listenForOccludedToLockscreen() { scope.launch { - keyguardInteractor.biometricUnlockState - .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) + keyguardInteractor.isKeyguardOccluded + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> - val (biometricUnlockState, keyguardState) = pair - if ( - keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState) - ) { + val (isOccluded, lastStartedKeyguardState) = pair + // Occlusion signals come from the framework, and should interrupt any + // existing transition + if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) { keyguardTransitionRepository.startTransition( TransitionInfo( name, - KeyguardState.AOD, - KeyguardState.GONE, + KeyguardState.OCCLUDED, + KeyguardState.LOCKSCREEN, getAnimator(), ) ) @@ -62,14 +88,15 @@ constructor( } } - private fun getAnimator(): ValueAnimator { + private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) - setDuration(TRANSITION_DURATION_MS) + setDuration(duration.inWholeMilliseconds) } } companion object { - private const val TRANSITION_DURATION_MS = 500L + private val DEFAULT_DURATION = 500.milliseconds + val TO_LOCKSCREEN_DURATION = 1183.milliseconds } } 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 6912e1d3558e..402c1793f0b2 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 @@ -22,12 +22,16 @@ 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.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.merge /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -52,8 +56,27 @@ constructor( * but not vice-versa. */ val isDreaming: Flow<Boolean> = repository.isDreaming + /** Whether the system is dreaming with an overlay active */ + val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay + + /** + * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means + * that doze mode is not running and DREAMING is ok to commence. + */ + val isAbleToDream: Flow<Boolean> = + merge(isDreaming, isDreamingWithOverlay) + .sample( + dozeTransitionModel, + { isDreaming, dozeTransitionModel -> + isDreaming && isDozeOff(dozeTransitionModel.to) + } + ) + .distinctUntilChanged() + /** Whether the keyguard is showing or not. */ val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing + /** Whether the keyguard is occluded (covered by an activity). */ + val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded /** Whether the keyguard is going away. */ val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway /** Whether the bouncer is showing or not. */ @@ -74,8 +97,8 @@ constructor( /** The approximate location on the screen of the face unlock sensor, if one is available. */ val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation - fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> { - return dozeTransitionModel.filter { it.to == state } + fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> { + return dozeTransitionModel.filter { states.contains(it.to) } } fun isKeyguardShowing(): Boolean { return repository.isKeyguardShowing() 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 bb8b79a3aa6b..fbed446b7d9a 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 @@ -37,13 +37,13 @@ constructor( // exhaustive val ret = when (it) { - is LockscreenBouncerTransitionInteractor -> Log.d(TAG, "Started $it") - 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") - is BouncerToGoneTransitionInteractor -> Log.d(TAG, "Started $it") - is DreamingTransitionInteractor -> Log.d(TAG, "Started $it") + is FromBouncerTransitionInteractor -> Log.d(TAG, "Started $it") + is FromAodTransitionInteractor -> Log.d(TAG, "Started $it") + is FromGoneTransitionInteractor -> Log.d(TAG, "Started $it") + is FromLockscreenTransitionInteractor -> Log.d(TAG, "Started $it") + is FromDreamingTransitionInteractor -> Log.d(TAG, "Started $it") + is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it") + is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it") } it.start() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index 6e25200bc2f6..a59c407182e2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -89,6 +89,7 @@ constructor( KeyguardState.BOUNCER -> true KeyguardState.LOCKSCREEN -> true KeyguardState.GONE -> true + KeyguardState.OCCLUDED -> true } } } 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 5f63ae765854..81fa2336d40d 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 @@ -32,25 +32,25 @@ abstract class StartKeyguardTransitionModule { @Binds @IntoSet - abstract fun lockscreenBouncer( - impl: LockscreenBouncerTransitionInteractor - ): TransitionInteractor + abstract fun fromBouncer(impl: FromBouncerTransitionInteractor): TransitionInteractor @Binds @IntoSet - abstract fun aodLockscreen(impl: AodLockscreenTransitionInteractor): TransitionInteractor + abstract fun fromLockscreen(impl: FromLockscreenTransitionInteractor): TransitionInteractor - @Binds @IntoSet abstract fun goneAod(impl: GoneAodTransitionInteractor): TransitionInteractor + @Binds @IntoSet abstract fun fromAod(impl: FromAodTransitionInteractor): TransitionInteractor - @Binds @IntoSet abstract fun aodGone(impl: AodToGoneTransitionInteractor): TransitionInteractor + @Binds @IntoSet abstract fun fromGone(impl: FromGoneTransitionInteractor): TransitionInteractor @Binds @IntoSet - abstract fun bouncerGone(impl: BouncerToGoneTransitionInteractor): TransitionInteractor + abstract fun fromDreaming(impl: FromDreamingTransitionInteractor): TransitionInteractor @Binds @IntoSet - abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor + abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor - @Binds @IntoSet abstract fun dreaming(impl: DreamingTransitionInteractor): TransitionInteractor + @Binds + @IntoSet + abstract fun fromDozing(impl: FromDozingTransitionInteractor): TransitionInteractor } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index 08ad3d5bdbf6..4d24c14501aa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -31,4 +31,6 @@ sealed class TransitionInteractor(val name: String) { abstract fun start() fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second) + + fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt index dd908c420fcb..c7579862a717 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt @@ -57,4 +57,8 @@ enum class KeyguardState { * with SWIPE security method or face unlock without bypass. */ GONE, + /* + * An activity is displaying over the keyguard. + */ + OCCLUDED, } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt index 402fac1c8fb4..e164f5d58b07 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION +import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.AnimationParams import javax.inject.Inject diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamCallbackControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt index 003efbfdc4d7..9f534ef14f54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamCallbackControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.dreams import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -30,23 +31,27 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) -class DreamCallbackControllerTest : SysuiTestCase() { +class DreamOverlayCallbackControllerTest : SysuiTestCase() { - @Mock private lateinit var callback: DreamCallbackController.DreamCallback + @Mock private lateinit var callback: DreamOverlayCallbackController.Callback - private lateinit var underTest: DreamCallbackController + private lateinit var underTest: DreamOverlayCallbackController @Before fun setUp() { MockitoAnnotations.initMocks(this) - underTest = DreamCallbackController() + underTest = DreamOverlayCallbackController() } @Test - fun testOnWakeUpInvokesCallback() { + fun onWakeUpInvokesCallback() { + underTest.onStartDream() + assertThat(underTest.isDreaming).isEqualTo(true) + underTest.addCallback(callback) underTest.onWakeUp() verify(callback).onWakeUp() + assertThat(underTest.isDreaming).isEqualTo(false) // Adding twice should not invoke twice reset(callback) @@ -60,4 +65,27 @@ class DreamCallbackControllerTest : SysuiTestCase() { underTest.onWakeUp() verify(callback, never()).onWakeUp() } + + @Test + fun onStartDreamInvokesCallback() { + underTest.addCallback(callback) + + assertThat(underTest.isDreaming).isEqualTo(false) + + underTest.onStartDream() + verify(callback).onStartDream() + assertThat(underTest.isDreaming).isEqualTo(true) + + // Adding twice should not invoke twice + reset(callback) + underTest.addCallback(callback) + underTest.onStartDream() + verify(callback, times(1)).onStartDream() + + // After remove, no call to callback + reset(callback) + underTest.removeCallback(callback) + underTest.onStartDream() + verify(callback, never()).onStartDream() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index d6f8deabc270..4568d1e9b3bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -114,7 +114,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { UiEventLogger mUiEventLogger; @Mock - DreamCallbackController mDreamCallbackController; + DreamOverlayCallbackController mDreamOverlayCallbackController; @Captor ArgumentCaptor<View> mViewCaptor; @@ -145,7 +145,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mKeyguardUpdateMonitor, mUiEventLogger, LOW_LIGHT_COMPONENT, - mDreamCallbackController); + mDreamOverlayCallbackController); } @Test @@ -357,7 +357,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mService.onWakeUp(callback); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor); - verify(mDreamCallbackController).onWakeUp(); + verify(mDreamOverlayCallbackController).onWakeUp(); } @Test 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 563d44d3f15f..be712f699b7b 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 @@ -28,8 +28,7 @@ import com.android.systemui.doze.DozeHost import com.android.systemui.doze.DozeMachine import com.android.systemui.doze.DozeTransitionCallback import com.android.systemui.doze.DozeTransitionListener -import com.android.systemui.dreams.DreamCallbackController -import com.android.systemui.dreams.DreamCallbackController.DreamCallback +import com.android.systemui.dreams.DreamOverlayCallbackController import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockSource @@ -68,7 +67,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var dozeTransitionListener: DozeTransitionListener @Mock private lateinit var authController: AuthController @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var dreamCallbackController: DreamCallbackController + @Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController private lateinit var underTest: KeyguardRepositoryImpl @@ -86,7 +85,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { keyguardUpdateMonitor, dozeTransitionListener, authController, - dreamCallbackController, + dreamOverlayCallbackController, ) } @@ -171,6 +170,29 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test + fun isKeyguardOccluded() = + runTest(UnconfinedTestDispatcher()) { + whenever(keyguardStateController.isOccluded).thenReturn(false) + var latest: Boolean? = null + val job = underTest.isKeyguardOccluded.onEach { latest = it }.launchIn(this) + + assertThat(latest).isFalse() + + val captor = argumentCaptor<KeyguardStateController.Callback>() + verify(keyguardStateController).addCallback(captor.capture()) + + whenever(keyguardStateController.isOccluded).thenReturn(true) + captor.value.onKeyguardShowingChanged() + assertThat(latest).isTrue() + + whenever(keyguardStateController.isOccluded).thenReturn(false) + captor.value.onKeyguardShowingChanged() + assertThat(latest).isFalse() + + job.cancel() + } + + @Test fun isDozing() = runTest(UnconfinedTestDispatcher()) { var latest: Boolean? = null @@ -343,19 +365,22 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test - fun isDreamingFromDreamCallbackController() = + fun isDreamingFromDreamOverlayCallbackController() = runTest(UnconfinedTestDispatcher()) { - whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(true) + whenever(dreamOverlayCallbackController.isDreaming).thenReturn(false) var latest: Boolean? = null - val job = underTest.isDreaming.onEach { latest = it }.launchIn(this) + val job = underTest.isDreamingWithOverlay.onEach { latest = it }.launchIn(this) - assertThat(latest).isTrue() + assertThat(latest).isFalse() val listener = - withArgCaptor<DreamCallbackController.DreamCallback> { - verify(dreamCallbackController).addCallback(capture()) + withArgCaptor<DreamOverlayCallbackController.Callback> { + verify(dreamOverlayCallbackController).addCallback(capture()) } + listener.onStartDream() + assertThat(latest).isTrue() + listener.onWakeUp() assertThat(latest).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index a6cf84053861..d2b7838274a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -23,6 +23,8 @@ import com.android.systemui.animation.Interpolators import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl +import com.android.systemui.keyguard.shared.model.DozeStateModel +import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.WakeSleepReason @@ -42,6 +44,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.Mock +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -64,8 +67,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // Used to verify transition requests for test output @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository - private lateinit var lockscreenBouncerTransitionInteractor: - LockscreenBouncerTransitionInteractor + private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor + private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor @Before fun setUp() { @@ -79,25 +82,82 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { transitionRepository = KeyguardTransitionRepositoryImpl() runner = KeyguardTransitionRunner(transitionRepository) - lockscreenBouncerTransitionInteractor = - LockscreenBouncerTransitionInteractor( + fromLockscreenTransitionInteractor = + FromLockscreenTransitionInteractor( scope = testScope, keyguardInteractor = KeyguardInteractor(keyguardRepository), shadeRepository = shadeRepository, keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), ) - lockscreenBouncerTransitionInteractor.start() + fromLockscreenTransitionInteractor.start() + + fromDreamingTransitionInteractor = + FromDreamingTransitionInteractor( + scope = testScope, + keyguardInteractor = KeyguardInteractor(keyguardRepository), + keyguardTransitionRepository = mockTransitionRepository, + keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), + ) + fromDreamingTransitionInteractor.start() } @Test + fun `DREAMING to LOCKSCREEN`() = + testScope.runTest { + // GIVEN a device is dreaming and occluded + keyguardRepository.setDreamingWithOverlay(true) + keyguardRepository.setKeyguardOccluded(true) + runCurrent() + + // GIVEN a prior transition has run to DREAMING + runner.startTransition( + testScope, + TransitionInfo( + ownerName = "", + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + animator = + ValueAnimator().apply { + duration = 10 + interpolator = Interpolators.LINEAR + }, + ) + ) + runCurrent() + reset(mockTransitionRepository) + + // WHEN doze is complete + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH) + ) + // AND dreaming has stopped + keyguardRepository.setDreamingWithOverlay(false) + // AND occluded has stopped + keyguardRepository.setKeyguardOccluded(false) + runCurrent() + + val info = + withArgCaptor<TransitionInfo> { + verify(mockTransitionRepository).startTransition(capture()) + } + // THEN a transition to BOUNCER should occur + assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor") + assertThat(info.from).isEqualTo(KeyguardState.DREAMING) + assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN) + assertThat(info.animator).isNotNull() + + coroutineContext.cancelChildren() + } + + @Test fun `LOCKSCREEN to BOUNCER via bouncer showing call`() = testScope.runTest { // GIVEN a device that has at least woken up keyguardRepository.setWakefulnessModel(startingToWake()) runCurrent() - // GIVEN a transition has run to LOCKSCREEN + // GIVEN a prior transition has run to LOCKSCREEN runner.startTransition( testScope, TransitionInfo( @@ -122,7 +182,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { verify(mockTransitionRepository).startTransition(capture()) } // THEN a transition to BOUNCER should occur - assertThat(info.ownerName).isEqualTo("LockscreenBouncerTransitionInteractor") + assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN) assertThat(info.to).isEqualTo(KeyguardState.BOUNCER) assertThat(info.animator).isNotNull() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt index c54e456416c7..557166301d64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt @@ -21,7 +21,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION +import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.AnimationParams import com.android.systemui.keyguard.shared.model.KeyguardState 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 55019490bdcd..39d2ecaef51a 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 @@ -46,12 +46,18 @@ class FakeKeyguardRepository : KeyguardRepository { private val _isKeyguardShowing = MutableStateFlow(false) override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing + private val _isKeyguardOccluded = MutableStateFlow(false) + override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded + private val _isDozing = MutableStateFlow(false) override val isDozing: Flow<Boolean> = _isDozing private val _isDreaming = MutableStateFlow(false) override val isDreaming: Flow<Boolean> = _isDreaming + private val _isDreamingWithOverlay = MutableStateFlow(false) + override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay + private val _dozeAmount = MutableStateFlow(0f) override val linearDozeAmount: Flow<Float> = _dozeAmount @@ -112,10 +118,18 @@ class FakeKeyguardRepository : KeyguardRepository { _isKeyguardShowing.value = isShowing } + fun setKeyguardOccluded(isOccluded: Boolean) { + _isKeyguardOccluded.value = isOccluded + } + fun setDozing(isDozing: Boolean) { _isDozing.value = isDozing } + fun setDreamingWithOverlay(isDreaming: Boolean) { + _isDreamingWithOverlay.value = isDreaming + } + fun setDozeAmount(dozeAmount: Float) { _dozeAmount.value = dozeAmount } @@ -144,6 +158,10 @@ class FakeKeyguardRepository : KeyguardRepository { _fingerprintSensorLocation.tryEmit(location) } + fun setDozeTransitionModel(model: DozeTransitionModel) { + _dozeTransitionModel.value = model + } + override fun isUdfpsSupported(): Boolean { return _isUdfpsSupported.value } |