Merge changes I0808f8b8,I03c9f251,I6509e037,I0e3adb67 into tm-qpr-dev

* changes:
  Dreaming transitions - Enable teamfood
  Transitions - Update for canceled state
  Transitions - Handle cancel of bouncer swipe
  Transitions: Gone->Dreaming
This commit is contained in:
TreeHugger Robot
2023-01-12 18:29:52 +00:00
committed by Android (Google) Code Review
16 changed files with 416 additions and 56 deletions

View File

@@ -1292,6 +1292,9 @@
<!-- LOCKSCREEN -> DREAMING transition: Amount to shift lockscreen content on entering --> <!-- LOCKSCREEN -> DREAMING transition: Amount to shift lockscreen content on entering -->
<dimen name="lockscreen_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen> <dimen name="lockscreen_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen>
<!-- GONE -> DREAMING transition: Amount to shift lockscreen content on entering -->
<dimen name="gone_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen>
<!-- LOCKSCREEN -> OCCLUDED transition: Amount to shift lockscreen content on entering --> <!-- LOCKSCREEN -> OCCLUDED transition: Amount to shift lockscreen content on entering -->
<dimen name="lockscreen_to_occluded_transition_lockscreen_translation_y">-40dp</dimen> <dimen name="lockscreen_to_occluded_transition_lockscreen_translation_y">-40dp</dimen>

View File

@@ -200,7 +200,7 @@ object Flags {
/** A different path for unocclusion transitions back to keyguard */ /** A different path for unocclusion transitions back to keyguard */
// TODO(b/262859270): Tracking Bug // TODO(b/262859270): Tracking Bug
@JvmField @JvmField
val UNOCCLUSION_TRANSITION = unreleasedFlag(223, "unocclusion_transition", teamfood = false) val UNOCCLUSION_TRANSITION = unreleasedFlag(223, "unocclusion_transition", teamfood = true)
// flag for controlling auto pin confirmation and material u shapes in bouncer // flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField @JvmField

View File

@@ -209,7 +209,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
return return
} }
if (state == TransitionState.FINISHED) { if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
updateTransitionId = null updateTransitionId = null
} }

View File

@@ -21,7 +21,7 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 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.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel 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.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -56,7 +56,7 @@ constructor(
scope.launch { scope.launch {
// Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which // Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which
// otherwise would have gone through OCCLUDED first // otherwise would have gone through OCCLUDED first
keyguardInteractor.isDreamingWithOverlay keyguardInteractor.isAbleToDream
.sample( .sample(
combine( combine(
keyguardInteractor.dozeTransitionModel, keyguardInteractor.dozeTransitionModel,
@@ -65,8 +65,7 @@ constructor(
), ),
::toTriple ::toTriple
) )
.collect { triple -> .collect { (isDreaming, dozeTransitionModel, lastStartedTransition) ->
val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple
if ( if (
!isDreaming && !isDreaming &&
isDozeOff(dozeTransitionModel.to) && isDozeOff(dozeTransitionModel.to) &&
@@ -96,8 +95,7 @@ constructor(
), ),
::toTriple ::toTriple
) )
.collect { triple -> .collect { (isDreaming, isOccluded, lastStartedTransition) ->
val (isDreaming, isOccluded, lastStartedTransition) = triple
if ( if (
isOccluded && isOccluded &&
!isDreaming && !isDreaming &&
@@ -123,24 +121,18 @@ constructor(
private fun listenForDreamingToGone() { private fun listenForDreamingToGone() {
scope.launch { scope.launch {
keyguardInteractor.biometricUnlockState keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
.sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair) if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
.collect { pair -> keyguardTransitionRepository.startTransition(
val (biometricUnlockState, keyguardState) = pair TransitionInfo(
if ( name,
keyguardState == KeyguardState.DREAMING && KeyguardState.DREAMING,
isWakeAndUnlock(biometricUnlockState) KeyguardState.GONE,
) { getAnimator(),
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.DREAMING,
KeyguardState.GONE,
getAnimator(),
)
) )
} )
} }
}
} }
} }
@@ -151,8 +143,7 @@ constructor(
keyguardTransitionInteractor.finishedKeyguardState, keyguardTransitionInteractor.finishedKeyguardState,
::Pair ::Pair
) )
.collect { pair -> .collect { (dozeTransitionModel, keyguardState) ->
val (dozeTransitionModel, keyguardState) = pair
if ( if (
dozeTransitionModel.to == DozeStateModel.DOZE && dozeTransitionModel.to == DozeStateModel.DOZE &&
keyguardState == KeyguardState.DREAMING keyguardState == KeyguardState.DREAMING

View File

@@ -48,8 +48,6 @@ constructor(
private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository,
) : TransitionInteractor(FromLockscreenTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromLockscreenTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null
override fun start() { override fun start() {
listenForLockscreenToGone() listenForLockscreenToGone()
listenForLockscreenToOccluded() listenForLockscreenToOccluded()
@@ -104,6 +102,7 @@ constructor(
/* Starts transitions when manually dragging up the bouncer from the lockscreen. */ /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
private fun listenForLockscreenToBouncerDragging() { private fun listenForLockscreenToBouncerDragging() {
var transitionId: UUID? = null
scope.launch { scope.launch {
shadeRepository.shadeModel shadeRepository.shadeModel
.sample( .sample(
@@ -114,25 +113,43 @@ constructor(
), ),
::toTriple ::toTriple
) )
.collect { triple -> .collect { (shadeModel, keyguardState, statusBarState) ->
val (shadeModel, keyguardState, statusBarState) = triple
val id = transitionId val id = transitionId
if (id != null) { if (id != null) {
// An existing `id` means a transition is started, and calls to // An existing `id` means a transition is started, and calls to
// `updateTransition` will control it until FINISHED // `updateTransition` will control it until FINISHED or CANCELED
keyguardTransitionRepository.updateTransition( var nextState =
id, if (shadeModel.expansionAmount == 0f) {
1f - shadeModel.expansionAmount,
if (
shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f
) {
transitionId = null
TransitionState.FINISHED TransitionState.FINISHED
} else if (shadeModel.expansionAmount == 1f) {
TransitionState.CANCELED
} else { } else {
TransitionState.RUNNING TransitionState.RUNNING
} }
keyguardTransitionRepository.updateTransition(
id,
1f - shadeModel.expansionAmount,
nextState,
) )
if (
nextState == TransitionState.CANCELED ||
nextState == TransitionState.FINISHED
) {
transitionId = null
}
// If canceled, just put the state back
if (nextState == TransitionState.CANCELED) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.BOUNCER,
to = KeyguardState.LOCKSCREEN,
animator = getAnimator(0.milliseconds)
)
)
}
} else { } else {
// TODO (b/251849525): Remove statusbarstate check when that state is // TODO (b/251849525): Remove statusbarstate check when that state is
// integrated into KeyguardTransitionRepository // integrated into KeyguardTransitionRepository

View File

@@ -32,12 +32,15 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.CommandQueue.Callbacks import com.android.systemui.statusbar.CommandQueue.Callbacks
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
/** /**
@@ -89,15 +92,23 @@ constructor(
/** /**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means * 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. * that doze mode is not running and DREAMING is ok to commence.
*
* Allow a brief moment to prevent rapidly oscillating between true/false signals.
*/ */
val isAbleToDream: Flow<Boolean> = val isAbleToDream: Flow<Boolean> =
merge(isDreaming, isDreamingWithOverlay) merge(isDreaming, isDreamingWithOverlay)
.sample( .combine(
dozeTransitionModel, dozeTransitionModel,
{ isDreaming, dozeTransitionModel -> { isDreaming, dozeTransitionModel ->
isDreaming && isDozeOff(dozeTransitionModel.to) isDreaming && isDozeOff(dozeTransitionModel.to)
} }
) )
.flatMapLatest { isAbleToDream ->
flow {
delay(50)
emit(isAbleToDream)
}
}
.distinctUntilChanged() .distinctUntilChanged()
/** Whether the keyguard is showing or not. */ /** Whether the keyguard is showing or not. */

View File

@@ -22,13 +22,17 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.AnimationParams import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
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.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
import kotlin.time.Duration import kotlin.time.Duration
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@@ -53,9 +57,16 @@ constructor(
val dreamingToLockscreenTransition: Flow<TransitionStep> = val dreamingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING, LOCKSCREEN) repository.transition(DREAMING, LOCKSCREEN)
/** GONE->DREAMING transition information. */
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
/** LOCKSCREEN->AOD transition information. */ /** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD) val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
/** LOCKSCREEN->BOUNCER transition information. */
val lockscreenToBouncerTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, BOUNCER)
/** LOCKSCREEN->DREAMING transition information. */ /** LOCKSCREEN->DREAMING transition information. */
val lockscreenToDreamingTransition: Flow<TransitionStep> = val lockscreenToDreamingTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING) repository.transition(LOCKSCREEN, DREAMING)
@@ -106,13 +117,23 @@ constructor(
): Flow<Float> { ): Flow<Float> {
val start = (params.startTime / totalDuration).toFloat() val start = (params.startTime / totalDuration).toFloat()
val chunks = (totalDuration / params.duration).toFloat() val chunks = (totalDuration / params.duration).toFloat()
var isRunning = false
return flow return flow
// When starting, emit a value of 0f to give animations a chance to set initial state
.map { step -> .map { step ->
val value = (step.value - start) * chunks
if (step.transitionState == STARTED) { if (step.transitionState == STARTED) {
0f // When starting, make sure to always emit. If a transition is started from the
// middle, it is possible this animation is being skipped but we need to inform
// the ViewModels of the last update
isRunning = true
max(0f, min(1f, value))
} else if (isRunning && value >= 1f) {
// Always send a final value of 1. Because of rounding, [value] may never be
// exactly 1.
isRunning = false
1f
} else { } else {
(step.value - start) * chunks value
} }
} }
.filter { value -> value >= 0f && value <= 1f } .filter { value -> value >= 0f && value <= 1f }

View File

@@ -22,10 +22,14 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.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.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/** /**
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to * Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -49,9 +53,15 @@ constructor(
/** Lockscreen views y-translation */ /** Lockscreen views y-translation */
fun lockscreenTranslationY(translatePx: Int): Flow<Float> { fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value -> return merge(
-translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx) flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
} -translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
},
// On end, reset the translation to 0
interactor.dreamingToLockscreenTransition
.filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
.map { 0f }
)
} }
/** Lockscreen views alpha */ /** Lockscreen views alpha */

View File

@@ -0,0 +1,69 @@
/*
* 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.animation.Interpolators.EMPHASIZED_ACCELERATE
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.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/** Breaks down GONE->DREAMING transition into discrete steps for corresponding views to consume. */
@SysUISingleton
class GoneToDreamingTransitionViewModel
@Inject
constructor(
private val interactor: KeyguardTransitionInteractor,
) {
/** Lockscreen views y-translation */
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
return merge(
flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
(EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
},
// On end, reset the translation to 0
interactor.goneToDreamingTransition
.filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
.map { 0f }
)
}
/** Lockscreen views alpha */
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
return interactor.transitionStepAnimation(
interactor.goneToDreamingTransition,
params,
totalDuration = TO_DREAMING_DURATION
)
}
companion object {
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
}
}

View File

@@ -21,7 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -48,7 +49,7 @@ constructor(
}, },
// On end, reset the translation to 0 // On end, reset the translation to 0
interactor.lockscreenToDreamingTransition interactor.lockscreenToDreamingTransition
.filter { step -> step.transitionState == TransitionState.FINISHED } .filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
.map { 0f } .map { 0f }
) )
} }

View File

@@ -144,6 +144,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -692,6 +693,7 @@ public final class NotificationPanelViewController implements Dumpable {
private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private KeyguardTransitionInteractor mKeyguardTransitionInteractor; private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -700,6 +702,7 @@ public final class NotificationPanelViewController implements Dumpable {
private int mDreamingToLockscreenTransitionTranslationY; private int mDreamingToLockscreenTransitionTranslationY;
private int mOccludedToLockscreenTransitionTranslationY; private int mOccludedToLockscreenTransitionTranslationY;
private int mLockscreenToDreamingTransitionTranslationY; private int mLockscreenToDreamingTransitionTranslationY;
private int mGoneToDreamingTransitionTranslationY;
private int mLockscreenToOccludedTransitionTranslationY; private int mLockscreenToOccludedTransitionTranslationY;
private boolean mUnocclusionTransitionFlagEnabled = false; private boolean mUnocclusionTransitionFlagEnabled = false;
@@ -735,6 +738,12 @@ public final class NotificationPanelViewController implements Dumpable {
step.getTransitionState() == TransitionState.RUNNING; step.getTransitionState() == TransitionState.RUNNING;
}; };
private final Consumer<TransitionStep> mGoneToDreamingTransition =
(TransitionStep step) -> {
mIsOcclusionTransitionRunning =
step.getTransitionState() == TransitionState.RUNNING;
};
private final Consumer<TransitionStep> mLockscreenToOccludedTransition = private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
(TransitionStep step) -> { (TransitionStep step) -> {
mIsOcclusionTransitionRunning = mIsOcclusionTransitionRunning =
@@ -813,6 +822,7 @@ public final class NotificationPanelViewController implements Dumpable {
DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel, DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel, OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel, LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel, LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
@Main CoroutineDispatcher mainDispatcher, @Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor, KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -834,6 +844,7 @@ public final class NotificationPanelViewController implements Dumpable {
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel; mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel; mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel; mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel; mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor; mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -1172,6 +1183,17 @@ public final class NotificationPanelViewController implements Dumpable {
setTransitionY(mNotificationStackScrollLayoutController), setTransitionY(mNotificationStackScrollLayoutController),
mMainDispatcher); mMainDispatcher);
// Gone->Dreaming
collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
mGoneToDreamingTransition, mMainDispatcher);
collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController),
mMainDispatcher);
collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY(
mGoneToDreamingTransitionTranslationY),
setTransitionY(mNotificationStackScrollLayoutController),
mMainDispatcher);
// Lockscreen->Occluded // Lockscreen->Occluded
collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
mLockscreenToOccludedTransition, mMainDispatcher); mLockscreenToOccludedTransition, mMainDispatcher);
@@ -1223,6 +1245,8 @@ public final class NotificationPanelViewController implements Dumpable {
R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y); R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y);
mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize( mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y); R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y);
mGoneToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
R.dimen.gone_to_dreaming_transition_lockscreen_translation_y);
mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize( mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize(
R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y); R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y);
} }

View File

@@ -168,6 +168,25 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(wtfHandler.failed).isTrue() assertThat(wtfHandler.failed).isTrue()
} }
@Test
fun `Attempt to manually update transition after CANCELED state throws exception`() {
val uuid =
underTest.startTransition(
TransitionInfo(
ownerName = OWNER_NAME,
from = AOD,
to = LOCKSCREEN,
animator = null,
)
)
checkNotNull(uuid).let {
underTest.updateTransition(it, 0.2f, TransitionState.CANCELED)
underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
}
assertThat(wtfHandler.failed).isTrue()
}
private fun listWithStep( private fun listWithStep(
step: BigDecimal, step: BigDecimal,
start: BigDecimal = BigDecimal.ZERO, start: BigDecimal = BigDecimal.ZERO,

View File

@@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Before import org.junit.Before
@@ -177,7 +178,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardRepository.setDreamingWithOverlay(false) keyguardRepository.setDreamingWithOverlay(false)
// AND occluded has stopped // AND occluded has stopped
keyguardRepository.setKeyguardOccluded(false) keyguardRepository.setKeyguardOccluded(false)
runCurrent() advanceUntilIdle()
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
@@ -506,7 +507,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture())
} }
// THEN a transition to DOZING should occur // THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.GONE) assertThat(info.from).isEqualTo(KeyguardState.GONE)
assertThat(info.to).isEqualTo(KeyguardState.AOD) assertThat(info.to).isEqualTo(KeyguardState.AOD)
@@ -515,6 +516,49 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren() coroutineContext.cancelChildren()
} }
@Test
fun `GONE to DREAMING`() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
runCurrent()
// GIVEN a prior transition has run to GONE
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
reset(mockTransitionRepository)
// WHEN the device begins to dream
keyguardRepository.setDreamingWithOverlay(true)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DREAMING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.GONE)
assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
private fun startingToWake() = private fun startingToWake() =
WakefulnessModel( WakefulnessModel(
WakefulnessState.STARTING_TO_WAKE, WakefulnessState.STARTING_TO_WAKE,

View File

@@ -0,0 +1,141 @@
/*
* 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 androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
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.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: GoneToDreamingTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
underTest = GoneToDreamingTransitionViewModel(interactor)
}
@Test
fun lockscreenFadeOut() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
// Should start running here...
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.2f))
// ...up to here
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(1f))
// Only three values should be present, since the dream overlay runs for a small
// fraction
// of the overall animation time
assertThat(values.size).isEqualTo(3)
assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA))
job.cancel()
}
@Test
fun lockscreenTranslationY() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val pixels = 100
val job =
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
// Should start running here...
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
// ...up to here
repository.sendTransitionStep(step(1f))
// And a final reset event on CANCEL
repository.sendTransitionStep(step(0.8f, TransitionState.CANCELED))
assertThat(values.size).isEqualTo(4)
assertThat(values[0])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
animValue(0f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
assertThat(values[1])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
assertThat(values[2])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
assertThat(values[3]).isEqualTo(0f)
job.cancel()
}
private fun animValue(stepValue: Float, params: AnimationParams): Float {
val totalDuration = TO_DREAMING_DURATION
val startValue = (params.startTime / totalDuration).toFloat()
val multiplier = (totalDuration / params.duration).toFloat()
return (stepValue - startValue) * multiplier
}
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
): TransitionStep {
return TransitionStep(
from = KeyguardState.GONE,
to = KeyguardState.DREAMING,
value = value,
transitionState = state,
ownerName = "GoneToDreamingTransitionViewModelTest"
)
}
}

View File

@@ -67,8 +67,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
repository.sendTransitionStep(step(1f)) repository.sendTransitionStep(step(1f))
// Only three values should be present, since the dream overlay runs for a small // Only three values should be present, since the dream overlay runs for a small
// fraction // fraction of the overall animation time
// of the overall animation time
assertThat(values.size).isEqualTo(3) assertThat(values.size).isEqualTo(3)
assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA)) assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA)) assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
@@ -92,8 +91,10 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
repository.sendTransitionStep(step(0.5f)) repository.sendTransitionStep(step(0.5f))
// ...up to here // ...up to here
repository.sendTransitionStep(step(1f)) repository.sendTransitionStep(step(1f))
// And a final reset event on FINISHED
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
assertThat(values.size).isEqualTo(3) assertThat(values.size).isEqualTo(4)
assertThat(values[0]) assertThat(values[0])
.isEqualTo( .isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation( EMPHASIZED_ACCELERATE.getInterpolation(
@@ -112,6 +113,8 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y) animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
) * pixels ) * pixels
) )
assertThat(values[3]).isEqualTo(0f)
job.cancel() job.cancel()
} }
@@ -123,12 +126,15 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
return (stepValue - startValue) * multiplier return (stepValue - startValue) * multiplier
} }
private fun step(value: Float): TransitionStep { private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
): TransitionStep {
return TransitionStep( return TransitionStep(
from = KeyguardState.LOCKSCREEN, from = KeyguardState.LOCKSCREEN,
to = KeyguardState.DREAMING, to = KeyguardState.DREAMING,
value = value, value = value,
transitionState = TransitionState.RUNNING, transitionState = state,
ownerName = "LockscreenToDreamingTransitionViewModelTest" ownerName = "LockscreenToDreamingTransitionViewModelTest"
) )
} }

View File

@@ -107,6 +107,7 @@ import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteracto
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -298,6 +299,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; @Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
@Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; @Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
@Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; @Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
@Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private CoroutineDispatcher mMainDispatcher; @Mock private CoroutineDispatcher mMainDispatcher;
@@ -522,6 +524,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mDreamingToLockscreenTransitionViewModel, mDreamingToLockscreenTransitionViewModel,
mOccludedToLockscreenTransitionViewModel, mOccludedToLockscreenTransitionViewModel,
mLockscreenToDreamingTransitionViewModel, mLockscreenToDreamingTransitionViewModel,
mGoneToDreamingTransitionViewModel,
mLockscreenToOccludedTransitionViewModel, mLockscreenToOccludedTransitionViewModel,
mMainDispatcher, mMainDispatcher,
mKeyguardTransitionInteractor, mKeyguardTransitionInteractor,