Add transitions for the lockscreen hosted dream

Add a new KeyguardState for the lockscreen hosted dream, as
the transition animations to/from lockscreen/gone/bouncer are
different from the existing dreaming state. We may also add
animations for the lockscreen hosted dream<->lowlight dream
transition in later changes.
Add Gone->DreamingLockscreenHosted transition ViewModel and set
keyguard alpha to 0 immediately and don't animate the clock when
updating its position during the transition.
When dreaming active, the clock will always be centered.

Bug: b/285062308
Test: atest KeyguardTransitionScenariosTest
Test: atest NotificationPanelViewControllerTest
Change-Id: I6678318b9cbd89d8e47cdd04855c1a592dea5d71
This commit is contained in:
Coco Duan
2023-07-12 06:07:06 +00:00
parent 5b5df31e7b
commit 4b0a55e4bc
16 changed files with 672 additions and 22 deletions

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2023 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.app.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
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
class FromDreamingLockscreenHostedTransitionInteractor
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
override val transitionInteractor: KeyguardTransitionInteractor,
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
) {
override fun start() {
listenForDreamingLockscreenHostedToLockscreen()
listenForDreamingLockscreenHostedToGone()
listenForDreamingLockscreenHostedToDozing()
listenForDreamingLockscreenHostedToOccluded()
listenForDreamingLockscreenHostedToPrimaryBouncer()
}
private fun listenForDreamingLockscreenHostedToLockscreen() {
scope.launch {
keyguardInteractor.isActiveDreamLockscreenHosted
// Add a slight delay to prevent transitioning to lockscreen from happening too soon
// as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
.onEach { delay(50) }
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
transitionInteractor.startedKeyguardTransitionStep,
::Pair
),
::toTriple
)
.collect {
(isActiveDreamLockscreenHosted, dozeTransitionModel, lastStartedTransition) ->
if (
!isActiveDreamLockscreenHosted &&
DozeStateModel.isDozeOff(dozeTransitionModel.to) &&
lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
) {
startTransitionTo(KeyguardState.LOCKSCREEN)
}
}
}
}
private fun listenForDreamingLockscreenHostedToOccluded() {
scope.launch {
keyguardInteractor.isActiveDreamLockscreenHosted
.sample(
combine(
keyguardInteractor.isKeyguardOccluded,
transitionInteractor.startedKeyguardTransitionStep,
::Pair,
),
::toTriple
)
.collect { (isActiveDreamLockscreenHosted, isOccluded, lastStartedTransition) ->
if (
isOccluded &&
!isActiveDreamLockscreenHosted &&
lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
) {
startTransitionTo(KeyguardState.OCCLUDED)
}
}
}
}
private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (isBouncerShowing, lastStartedTransitionStep) ->
if (
isBouncerShowing &&
lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
) {
startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
}
}
}
}
private fun listenForDreamingLockscreenHostedToGone() {
scope.launch {
keyguardInteractor.biometricUnlockState
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (biometricUnlockState, lastStartedTransitionStep) ->
if (
lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED &&
biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
) {
startTransitionTo(KeyguardState.GONE)
}
}
}
}
private fun listenForDreamingLockscreenHostedToDozing() {
scope.launch {
combine(
keyguardInteractor.dozeTransitionModel,
transitionInteractor.startedKeyguardTransitionStep,
::Pair
)
.collect { (dozeTransitionModel, lastStartedTransitionStep) ->
if (
dozeTransitionModel.to == DozeStateModel.DOZE &&
lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
) {
startTransitionTo(KeyguardState.DOZING)
}
}
}
}
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
duration =
if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
else DEFAULT_DURATION.inWholeMilliseconds
}
}
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 1167.milliseconds
}
}

View File

@@ -94,11 +94,16 @@ constructor(
private fun listenForDreamingToGone() {
scope.launch {
keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
startTransitionTo(KeyguardState.GONE)
keyguardInteractor.biometricUnlockState
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (biometricUnlockState, lastStartedTransitionStep) ->
if (
lastStartedTransitionStep.to == KeyguardState.DREAMING &&
biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
) {
startTransitionTo(KeyguardState.GONE)
}
}
}
}
}

View File

@@ -48,6 +48,7 @@ constructor(
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
listenForGoneToLockscreen()
listenForGoneToDreamingLockscreenHosted()
}
// Primarily for when the user chooses to lock down the device
@@ -63,12 +64,35 @@ constructor(
}
}
private fun listenForGoneToDreamingLockscreenHosted() {
scope.launch {
keyguardInteractor.isActiveDreamLockscreenHosted
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (isActiveDreamLockscreenHosted, lastStartedStep) ->
if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) {
startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
}
}
}
}
private fun listenForGoneToDreaming() {
scope.launch {
keyguardInteractor.isAbleToDream
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (isAbleToDream, lastStartedStep) ->
if (isAbleToDream && lastStartedStep.to == KeyguardState.GONE) {
.sample(
combine(
transitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isActiveDreamLockscreenHosted,
::Pair
),
::toTriple
)
.collect { (isAbleToDream, lastStartedStep, isActiveDreamLockscreenHosted) ->
if (
isAbleToDream &&
lastStartedStep.to == KeyguardState.GONE &&
!isActiveDreamLockscreenHosted
) {
startTransitionTo(KeyguardState.DREAMING)
}
}

View File

@@ -70,17 +70,27 @@ constructor(
combine(
transitionInteractor.startedKeyguardTransitionStep,
transitionInteractor.finishedKeyguardState,
::Pair
keyguardInteractor.isActiveDreamLockscreenHosted,
::Triple
),
::toTriple
::toQuad
)
.collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) ->
.collect {
(
isAbleToDream,
lastStartedTransition,
finishedKeyguardState,
isActiveDreamLockscreenHosted) ->
val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
val isTransitionInterruptible =
lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
!invalidFromStates.contains(lastStartedTransition.from)
if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) {
startTransitionTo(KeyguardState.DREAMING)
if (isActiveDreamLockscreenHosted) {
startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
} else {
startTransitionTo(KeyguardState.DREAMING)
}
}
}
}

View File

@@ -27,6 +27,8 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -52,6 +54,7 @@ constructor(
listenForPrimaryBouncerToGone()
listenForPrimaryBouncerToAodOrDozing()
listenForPrimaryBouncerToLockscreenOrOccluded()
listenForPrimaryBouncerToDreamingLockscreenHosted()
}
private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -62,17 +65,24 @@ constructor(
keyguardInteractor.wakefulnessModel,
transitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
::Triple
keyguardInteractor.isActiveDreamLockscreenHosted,
::toQuad
),
::toQuad
::toQuint
)
.collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, occluded)
->
.collect {
(
isBouncerShowing,
wakefulnessState,
lastStartedTransitionStep,
occluded,
isActiveDreamLockscreenHosted) ->
if (
!isBouncerShowing &&
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
(wakefulnessState.state == WakefulnessState.AWAKE ||
wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE)
wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE) &&
!isActiveDreamLockscreenHosted
) {
startTransitionTo(
if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
@@ -111,6 +121,30 @@ constructor(
}
}
private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
.sample(
combine(
keyguardInteractor.isActiveDreamLockscreenHosted,
transitionInteractor.startedKeyguardTransitionStep,
::Pair
),
::toTriple
)
.collect {
(isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
if (
!isBouncerShowing &&
isActiveDreamLockscreenHosted &&
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
) {
startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
}
}
}
}
private fun listenForPrimaryBouncerToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway

View File

@@ -36,7 +36,6 @@ import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -100,7 +99,7 @@ constructor(
/** Whether the system is dreaming with an overlay active */
val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
/** Whether the system is dreaming and the active dream is hosted in lockscreen */
val isActiveDreamLockscreenHosted: Flow<Boolean> = repository.isActiveDreamLockscreenHosted
val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
/** Event for when the camera gesture is detected */
val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
val callback =
@@ -237,8 +236,16 @@ constructor(
repository.setQuickSettingsVisible(isVisible)
}
fun setKeyguardRootVisibility(statusBarState: Int, goingToFullShade: Boolean, isOcclusionTransitionRunning: Boolean) {
repository.setKeyguardVisibility(statusBarState, goingToFullShade, isOcclusionTransitionRunning)
fun setKeyguardRootVisibility(
statusBarState: Int,
goingToFullShade: Boolean,
isOcclusionTransitionRunning: Boolean
) {
repository.setKeyguardVisibility(
statusBarState,
goingToFullShade,
isOcclusionTransitionRunning
)
}
fun setClockPosition(x: Int, y: Int) {

View File

@@ -45,6 +45,7 @@ constructor(
is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
is FromDreamingLockscreenHostedTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}

View File

@@ -24,6 +24,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
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.KeyguardState.OCCLUDED
@@ -89,12 +90,20 @@ constructor(
val dreamingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING, LOCKSCREEN)
/** DREAMING_LOCKSCREEN_HOSTED->LOCKSCREEN transition information. */
val dreamingLockscreenHostedToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)
/** GONE->AOD transition information. */
val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
/** GONE->DREAMING transition information. */
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
/** GONE->DREAMING_LOCKSCREEN_HOSTED transition information. */
val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
@@ -102,6 +111,10 @@ constructor(
val lockscreenToDreamingTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING)
/** LOCKSCREEN->DREAMING_LOCKSCREEN_HOSTED transition information. */
val lockscreenToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)
/** LOCKSCREEN->OCCLUDED transition information. */
val lockscreenToOccludedTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, OCCLUDED)

View File

@@ -92,6 +92,7 @@ constructor(
KeyguardState.DOZING -> false
KeyguardState.AOD -> false
KeyguardState.DREAMING -> true
KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
KeyguardState.ALTERNATE_BOUNCER -> true
KeyguardState.PRIMARY_BOUNCER -> true
KeyguardState.LOCKSCREEN -> true

View File

@@ -48,6 +48,12 @@ abstract class StartKeyguardTransitionModule {
@IntoSet
abstract fun fromDreaming(impl: FromDreamingTransitionInteractor): TransitionInteractor
@Binds
@IntoSet
abstract fun fromDreamingLockscreenHosted(
impl: FromDreamingLockscreenHostedTransitionInteractor
): TransitionInteractor
@Binds
@IntoSet
abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor

View File

@@ -35,6 +35,12 @@ enum class KeyguardState {
* parties to present their own UI over keyguard, like a screensaver.
*/
DREAMING,
/*
* A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
* It is a special version of DREAMING state but not DOZING. The active dream will be windowless
* and hosted in the lockscreen.
*/
DREAMING_LOCKSCREEN_HOSTED,
/**
* The device has entered a special low-power mode within SystemUI, also called the Always-on
* Display (AOD). A minimal UI is presented to show critical information. If the device is in

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 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.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
/**
* Breaks down GONE->DREAMING_LOCKSCREEN_HOSTED transition into discrete steps for corresponding
* views to consume.
*/
@SysUISingleton
class GoneToDreamingLockscreenHostedTransitionViewModel
@Inject
constructor(
interactor: KeyguardTransitionInteractor,
) {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_DREAMING_DURATION,
transitionFlow = interactor.goneToDreamingLockscreenHostedTransition,
)
/** Lockscreen views alpha - hide immediately */
val lockscreenAlpha: Flow<Float> =
transitionAnimation.createFlow(
duration = 1.milliseconds,
onStep = { 0f },
)
}

View File

@@ -112,6 +112,7 @@ open class UdfpsLockscreenViewModel(
KeyguardState.OFF,
KeyguardState.DOZING,
KeyguardState.DREAMING,
KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
KeyguardState.AOD,
KeyguardState.PRIMARY_BOUNCER,
KeyguardState.GONE,

View File

@@ -140,6 +140,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel;
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
@@ -597,6 +598,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
private final GoneToDreamingLockscreenHostedTransitionViewModel
mGoneToDreamingLockscreenHostedTransitionViewModel;
private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -605,6 +609,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private final CoroutineDispatcher mMainDispatcher;
private boolean mIsAnyMultiShadeExpanded;
private boolean mIsOcclusionTransitionRunning = false;
private boolean mIsGoneToDreamingLockscreenHostedTransitionRunning;
private int mDreamingToLockscreenTransitionTranslationY;
private int mOccludedToLockscreenTransitionTranslationY;
private int mLockscreenToDreamingTransitionTranslationY;
@@ -652,6 +657,25 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
step.getTransitionState() == TransitionState.RUNNING;
};
private final Consumer<TransitionStep> mGoneToDreamingLockscreenHostedTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
step.getTransitionState() == TransitionState.RUNNING;
mIsGoneToDreamingLockscreenHostedTransitionRunning = mIsOcclusionTransitionRunning;
};
private final Consumer<TransitionStep> mLockscreenToDreamingLockscreenHostedTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
step.getTransitionState() == TransitionState.RUNNING;
};
private final Consumer<TransitionStep> mDreamingLockscreenHostedToLockscreenTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
step.getTransitionState() == TransitionState.RUNNING;
};
private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
@@ -734,6 +758,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
GoneToDreamingLockscreenHostedTransitionViewModel
goneToDreamingLockscreenHostedTransitionViewModel,
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
@Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -761,6 +787,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
mGoneToDreamingLockscreenHostedTransitionViewModel =
goneToDreamingLockscreenHostedTransitionViewModel;
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mKeyguardInteractor = keyguardInteractor;
@@ -1091,6 +1119,24 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mDreamingToLockscreenTransitionTranslationY),
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Gone -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
.getGoneToDreamingLockscreenHostedTransition(),
mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController),
mMainDispatcher);
// Lockscreen -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
.getLockscreenToDreamingLockscreenHostedTransition(),
mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
// Dreaming hosted in lockscreen -> Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
.getDreamingLockscreenHostedToLockscreenTransition(),
mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
// Occluded->Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
mOccludedToLockscreenTransition, mMainDispatcher);
@@ -1496,13 +1542,18 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mAnimateNextPositionUpdate = false;
}
private boolean shouldAnimateKeyguardStatusViewAlignment() {
// Do not animate when transitioning from Gone->DreamingLockscreenHosted
return !mIsGoneToDreamingLockscreenHostedTransitionRunning;
}
private void updateClockAppearance() {
int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
shouldAnimateClockChange);
updateKeyguardStatusViewAlignment(/* animate= */true);
updateKeyguardStatusViewAlignment(/* animate= */shouldAnimateKeyguardStatusViewAlignment());
int userSwitcherHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
if (mKeyguardUserSwitcherController != null) {
@@ -1625,6 +1676,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// overlap.
return true;
}
if (isActiveDreamLockscreenHosted()) {
// Dreaming hosted in lockscreen, no "visible" notifications. Safe to center the clock.
return true;
}
if (mNotificationListContainer.hasPulsingNotifications()) {
// Pulsing notification appears on the right. Move clock left to avoid overlap.
return false;
@@ -1653,6 +1708,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return mDozing && mDozeParameters.getAlwaysOn();
}
private boolean isActiveDreamLockscreenHosted() {
return mKeyguardInteractor.isActiveDreamLockscreenHosted().getValue();
}
private boolean hasVisibleNotifications() {
return mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0

View File

@@ -89,6 +89,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
FromAlternateBouncerTransitionInteractor
private lateinit var fromPrimaryBouncerTransitionInteractor:
FromPrimaryBouncerTransitionInteractor
private lateinit var fromDreamingLockscreenHostedTransitionInteractor:
FromDreamingLockscreenHostedTransitionInteractor
@Before
fun setUp() {
@@ -140,6 +142,15 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
)
.apply { start() }
fromDreamingLockscreenHostedTransitionInteractor =
FromDreamingLockscreenHostedTransitionInteractor(
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
)
.apply { start() }
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
@@ -298,6 +309,38 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
@Test
fun lockscreenToDreamingLockscreenHosted() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
keyguardRepository.setWakefulnessModel(startingToWake())
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
runCurrent()
// GIVEN a prior transition has run to LOCKSCREEN
runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
// WHEN the device begins to dream and the dream is lockscreen hosted
keyguardRepository.setDreamingWithOverlay(true)
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun lockscreenToDozing() =
testScope.runTest {
@@ -352,6 +395,149 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
@Test
fun dreamingLockscreenHostedToLockscreen() =
testScope.runTest {
// GIVEN a device dreaming with the lockscreen hosted dream and not dozing
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
keyguardRepository.setWakefulnessModel(startingToWake())
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
runCurrent()
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
// WHEN the lockscreen hosted dream stops
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to Lockscreen should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun dreamingLockscreenHostedToGone() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
// WHEN biometrics succeeds with wake and unlock from dream mode
keyguardRepository.setBiometricUnlockState(
BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to Gone should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.to).isEqualTo(KeyguardState.GONE)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun dreamingLockscreenHostedToPrimaryBouncer() =
testScope.runTest {
// GIVEN a device dreaming with lockscreen hosted dream and not dozing
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent()
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
// WHEN the primary bouncer is set to show
bouncerRepository.setPrimaryShow(true)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun dreamingLockscreenHostedToDozing() =
testScope.runTest {
// GIVEN a device is dreaming with lockscreen hosted dream
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
runCurrent()
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
// WHEN the device begins to sleep
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.INITIALIZED, to = DozeStateModel.DOZE)
)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.to).isEqualTo(KeyguardState.DOZING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun dreamingLockscreenHostedToOccluded() =
testScope.runTest {
// GIVEN device is dreaming with lockscreen hosted dream and not occluded
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
keyguardRepository.setKeyguardOccluded(false)
runCurrent()
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
// WHEN the keyguard is occluded and the lockscreen hosted dream stops
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun dozingToLockscreen() =
testScope.runTest {
@@ -532,6 +718,38 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
@Test
fun goneToDreamingLockscreenHosted() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
keyguardRepository.setWakefulnessModel(startingToWake())
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
runCurrent()
// GIVEN a prior transition has run to GONE
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
// WHEN the device begins to dream with the lockscreen hosted dream
keyguardRepository.setDreamingWithOverlay(true)
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.GONE)
assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun alternateBouncerToPrimaryBouncer() =
testScope.runTest {
@@ -725,6 +943,34 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
@Test
fun primaryBouncerToDreamingLockscreenHosted() =
testScope.runTest {
// GIVEN device dreaming with the lockscreen hosted dream and not dozing
keyguardRepository.setIsActiveDreamLockscreenHosted(true)
keyguardRepository.setWakefulnessModel(startingToWake())
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
runTransition(KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.PRIMARY_BOUNCER)
// WHEN the primary bouncer stops showing and lockscreen hosted dream still active
bouncerRepository.setPrimaryShow(false)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(transitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition back to DREAMING_LOCKSCREEN_HOSTED should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun occludedToGone() =
testScope.runTest {

View File

@@ -99,6 +99,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
@@ -297,7 +298,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected LockscreenToOccludedTransitionViewModel
mLockscreenToOccludedTransitionViewModel;
@Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
@Mock protected GoneToDreamingLockscreenHostedTransitionViewModel
mGoneToDreamingLockscreenHostedTransitionViewModel;
@Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
@Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -477,6 +479,20 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
.thenReturn(emptyFlow());
// Gone->Dreaming lockscreen hosted
when(mKeyguardTransitionInteractor.getGoneToDreamingLockscreenHostedTransition())
.thenReturn(emptyFlow());
when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha())
.thenReturn(emptyFlow());
// Dreaming lockscreen hosted->Lockscreen
when(mKeyguardTransitionInteractor.getDreamingLockscreenHostedToLockscreenTransition())
.thenReturn(emptyFlow());
// Lockscreen->Dreaming lockscreen hosted
when(mKeyguardTransitionInteractor.getLockscreenToDreamingLockscreenHostedTransition())
.thenReturn(emptyFlow());
// Lockscreen->Occluded
when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition())
.thenReturn(emptyFlow());
@@ -612,6 +628,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mOccludedToLockscreenTransitionViewModel,
mLockscreenToDreamingTransitionViewModel,
mGoneToDreamingTransitionViewModel,
mGoneToDreamingLockscreenHostedTransitionViewModel,
mLockscreenToOccludedTransitionViewModel,
mMainDispatcher,
mKeyguardTransitionInteractor,