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:
committed by
Android (Google) Code Review
commit
0e0d95d6da
@@ -1292,6 +1292,9 @@
|
||||
<!-- LOCKSCREEN -> DREAMING transition: Amount to shift lockscreen content on entering -->
|
||||
<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 -->
|
||||
<dimen name="lockscreen_to_occluded_transition_lockscreen_translation_y">-40dp</dimen>
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ object Flags {
|
||||
/** A different path for unocclusion transitions back to keyguard */
|
||||
// TODO(b/262859270): Tracking Bug
|
||||
@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
|
||||
@JvmField
|
||||
|
||||
@@ -209,7 +209,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
|
||||
return
|
||||
}
|
||||
|
||||
if (state == TransitionState.FINISHED) {
|
||||
if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
|
||||
updateTransitionId = null
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +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.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.KeyguardState
|
||||
@@ -56,7 +56,7 @@ constructor(
|
||||
scope.launch {
|
||||
// Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which
|
||||
// otherwise would have gone through OCCLUDED first
|
||||
keyguardInteractor.isDreamingWithOverlay
|
||||
keyguardInteractor.isAbleToDream
|
||||
.sample(
|
||||
combine(
|
||||
keyguardInteractor.dozeTransitionModel,
|
||||
@@ -65,8 +65,7 @@ constructor(
|
||||
),
|
||||
::toTriple
|
||||
)
|
||||
.collect { triple ->
|
||||
val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple
|
||||
.collect { (isDreaming, dozeTransitionModel, lastStartedTransition) ->
|
||||
if (
|
||||
!isDreaming &&
|
||||
isDozeOff(dozeTransitionModel.to) &&
|
||||
@@ -96,8 +95,7 @@ constructor(
|
||||
),
|
||||
::toTriple
|
||||
)
|
||||
.collect { triple ->
|
||||
val (isDreaming, isOccluded, lastStartedTransition) = triple
|
||||
.collect { (isDreaming, isOccluded, lastStartedTransition) ->
|
||||
if (
|
||||
isOccluded &&
|
||||
!isDreaming &&
|
||||
@@ -123,24 +121,18 @@ constructor(
|
||||
|
||||
private fun listenForDreamingToGone() {
|
||||
scope.launch {
|
||||
keyguardInteractor.biometricUnlockState
|
||||
.sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
|
||||
.collect { pair ->
|
||||
val (biometricUnlockState, keyguardState) = pair
|
||||
if (
|
||||
keyguardState == KeyguardState.DREAMING &&
|
||||
isWakeAndUnlock(biometricUnlockState)
|
||||
) {
|
||||
keyguardTransitionRepository.startTransition(
|
||||
TransitionInfo(
|
||||
name,
|
||||
KeyguardState.DREAMING,
|
||||
KeyguardState.GONE,
|
||||
getAnimator(),
|
||||
)
|
||||
keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
|
||||
if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
|
||||
keyguardTransitionRepository.startTransition(
|
||||
TransitionInfo(
|
||||
name,
|
||||
KeyguardState.DREAMING,
|
||||
KeyguardState.GONE,
|
||||
getAnimator(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +143,7 @@ constructor(
|
||||
keyguardTransitionInteractor.finishedKeyguardState,
|
||||
::Pair
|
||||
)
|
||||
.collect { pair ->
|
||||
val (dozeTransitionModel, keyguardState) = pair
|
||||
.collect { (dozeTransitionModel, keyguardState) ->
|
||||
if (
|
||||
dozeTransitionModel.to == DozeStateModel.DOZE &&
|
||||
keyguardState == KeyguardState.DREAMING
|
||||
|
||||
@@ -48,8 +48,6 @@ constructor(
|
||||
private val keyguardTransitionRepository: KeyguardTransitionRepository,
|
||||
) : TransitionInteractor(FromLockscreenTransitionInteractor::class.simpleName!!) {
|
||||
|
||||
private var transitionId: UUID? = null
|
||||
|
||||
override fun start() {
|
||||
listenForLockscreenToGone()
|
||||
listenForLockscreenToOccluded()
|
||||
@@ -104,6 +102,7 @@ constructor(
|
||||
|
||||
/* Starts transitions when manually dragging up the bouncer from the lockscreen. */
|
||||
private fun listenForLockscreenToBouncerDragging() {
|
||||
var transitionId: UUID? = null
|
||||
scope.launch {
|
||||
shadeRepository.shadeModel
|
||||
.sample(
|
||||
@@ -114,25 +113,43 @@ constructor(
|
||||
),
|
||||
::toTriple
|
||||
)
|
||||
.collect { triple ->
|
||||
val (shadeModel, keyguardState, statusBarState) = triple
|
||||
|
||||
.collect { (shadeModel, keyguardState, statusBarState) ->
|
||||
val id = transitionId
|
||||
if (id != null) {
|
||||
// An existing `id` means a transition is started, and calls to
|
||||
// `updateTransition` will control it until FINISHED
|
||||
keyguardTransitionRepository.updateTransition(
|
||||
id,
|
||||
1f - shadeModel.expansionAmount,
|
||||
if (
|
||||
shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f
|
||||
) {
|
||||
transitionId = null
|
||||
// `updateTransition` will control it until FINISHED or CANCELED
|
||||
var nextState =
|
||||
if (shadeModel.expansionAmount == 0f) {
|
||||
TransitionState.FINISHED
|
||||
} else if (shadeModel.expansionAmount == 1f) {
|
||||
TransitionState.CANCELED
|
||||
} else {
|
||||
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 {
|
||||
// TODO (b/251849525): Remove statusbarstate check when that state is
|
||||
// integrated into KeyguardTransitionRepository
|
||||
|
||||
@@ -32,12 +32,15 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
|
||||
import com.android.systemui.keyguard.shared.model.WakefulnessModel
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.CommandQueue.Callbacks
|
||||
import com.android.systemui.util.kotlin.sample
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
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
|
||||
* 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> =
|
||||
merge(isDreaming, isDreamingWithOverlay)
|
||||
.sample(
|
||||
.combine(
|
||||
dozeTransitionModel,
|
||||
{ isDreaming, dozeTransitionModel ->
|
||||
isDreaming && isDozeOff(dozeTransitionModel.to)
|
||||
}
|
||||
)
|
||||
.flatMapLatest { isAbleToDream ->
|
||||
flow {
|
||||
delay(50)
|
||||
emit(isAbleToDream)
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
/** Whether the keyguard is showing or not. */
|
||||
|
||||
@@ -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.KeyguardState
|
||||
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.GONE
|
||||
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.TransitionState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
|
||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.time.Duration
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
@@ -53,9 +57,16 @@ constructor(
|
||||
val dreamingToLockscreenTransition: Flow<TransitionStep> =
|
||||
repository.transition(DREAMING, LOCKSCREEN)
|
||||
|
||||
/** GONE->DREAMING transition information. */
|
||||
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
|
||||
|
||||
/** LOCKSCREEN->AOD transition information. */
|
||||
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
|
||||
|
||||
/** LOCKSCREEN->BOUNCER transition information. */
|
||||
val lockscreenToBouncerTransition: Flow<TransitionStep> =
|
||||
repository.transition(LOCKSCREEN, BOUNCER)
|
||||
|
||||
/** LOCKSCREEN->DREAMING transition information. */
|
||||
val lockscreenToDreamingTransition: Flow<TransitionStep> =
|
||||
repository.transition(LOCKSCREEN, DREAMING)
|
||||
@@ -106,13 +117,23 @@ constructor(
|
||||
): Flow<Float> {
|
||||
val start = (params.startTime / totalDuration).toFloat()
|
||||
val chunks = (totalDuration / params.duration).toFloat()
|
||||
var isRunning = false
|
||||
return flow
|
||||
// When starting, emit a value of 0f to give animations a chance to set initial state
|
||||
.map { step ->
|
||||
val value = (step.value - start) * chunks
|
||||
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 {
|
||||
(step.value - start) * chunks
|
||||
value
|
||||
}
|
||||
}
|
||||
.filter { value -> value >= 0f && value <= 1f }
|
||||
|
||||
@@ -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.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 DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
|
||||
@@ -49,9 +53,15 @@ constructor(
|
||||
|
||||
/** Lockscreen views y-translation */
|
||||
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||
return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
|
||||
-translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
|
||||
}
|
||||
return merge(
|
||||
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 */
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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.KeyguardTransitionInteractor
|
||||
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 kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -48,7 +49,7 @@ constructor(
|
||||
},
|
||||
// On end, reset the translation to 0
|
||||
interactor.lockscreenToDreamingTransition
|
||||
.filter { step -> step.transitionState == TransitionState.FINISHED }
|
||||
.filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
|
||||
.map { 0f }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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.TransitionStep;
|
||||
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.LockscreenToDreamingTransitionViewModel;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
|
||||
@@ -692,6 +693,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
|
||||
private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
|
||||
private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
|
||||
private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
|
||||
private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
|
||||
|
||||
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||
@@ -700,6 +702,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
private int mDreamingToLockscreenTransitionTranslationY;
|
||||
private int mOccludedToLockscreenTransitionTranslationY;
|
||||
private int mLockscreenToDreamingTransitionTranslationY;
|
||||
private int mGoneToDreamingTransitionTranslationY;
|
||||
private int mLockscreenToOccludedTransitionTranslationY;
|
||||
private boolean mUnocclusionTransitionFlagEnabled = false;
|
||||
|
||||
@@ -735,6 +738,12 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
step.getTransitionState() == TransitionState.RUNNING;
|
||||
};
|
||||
|
||||
private final Consumer<TransitionStep> mGoneToDreamingTransition =
|
||||
(TransitionStep step) -> {
|
||||
mIsOcclusionTransitionRunning =
|
||||
step.getTransitionState() == TransitionState.RUNNING;
|
||||
};
|
||||
|
||||
private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
|
||||
(TransitionStep step) -> {
|
||||
mIsOcclusionTransitionRunning =
|
||||
@@ -813,6 +822,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
|
||||
OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
|
||||
LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
|
||||
GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
|
||||
LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
|
||||
@Main CoroutineDispatcher mainDispatcher,
|
||||
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
||||
@@ -834,6 +844,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
|
||||
mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
|
||||
mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
|
||||
mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
|
||||
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
|
||||
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
|
||||
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||
@@ -1172,6 +1183,17 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
setTransitionY(mNotificationStackScrollLayoutController),
|
||||
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
|
||||
collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
|
||||
mLockscreenToOccludedTransition, mMainDispatcher);
|
||||
@@ -1223,6 +1245,8 @@ public final class NotificationPanelViewController implements Dumpable {
|
||||
R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y);
|
||||
mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
|
||||
R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y);
|
||||
mGoneToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
|
||||
R.dimen.gone_to_dreaming_transition_lockscreen_translation_y);
|
||||
mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize(
|
||||
R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y);
|
||||
}
|
||||
|
||||
@@ -168,6 +168,25 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
|
||||
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(
|
||||
step: BigDecimal,
|
||||
start: BigDecimal = BigDecimal.ZERO,
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.withArgCaptor
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
@@ -177,7 +178,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
||||
keyguardRepository.setDreamingWithOverlay(false)
|
||||
// AND occluded has stopped
|
||||
keyguardRepository.setKeyguardOccluded(false)
|
||||
runCurrent()
|
||||
advanceUntilIdle()
|
||||
|
||||
val info =
|
||||
withArgCaptor<TransitionInfo> {
|
||||
@@ -506,7 +507,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
||||
withArgCaptor<TransitionInfo> {
|
||||
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.from).isEqualTo(KeyguardState.GONE)
|
||||
assertThat(info.to).isEqualTo(KeyguardState.AOD)
|
||||
@@ -515,6 +516,49 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
||||
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() =
|
||||
WakefulnessModel(
|
||||
WakefulnessState.STARTING_TO_WAKE,
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -67,8 +67,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
|
||||
repository.sendTransitionStep(step(1f))
|
||||
|
||||
// Only three values should be present, since the dream overlay runs for a small
|
||||
// fraction
|
||||
// of the overall animation time
|
||||
// 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))
|
||||
@@ -92,8 +91,10 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
|
||||
repository.sendTransitionStep(step(0.5f))
|
||||
// ...up to here
|
||||
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])
|
||||
.isEqualTo(
|
||||
EMPHASIZED_ACCELERATE.getInterpolation(
|
||||
@@ -112,6 +113,8 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
|
||||
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
|
||||
) * pixels
|
||||
)
|
||||
assertThat(values[3]).isEqualTo(0f)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@@ -123,12 +126,15 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
|
||||
return (stepValue - startValue) * multiplier
|
||||
}
|
||||
|
||||
private fun step(value: Float): TransitionStep {
|
||||
private fun step(
|
||||
value: Float,
|
||||
state: TransitionState = TransitionState.RUNNING
|
||||
): TransitionStep {
|
||||
return TransitionStep(
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.DREAMING,
|
||||
value = value,
|
||||
transitionState = TransitionState.RUNNING,
|
||||
transitionState = state,
|
||||
ownerName = "LockscreenToDreamingTransitionViewModelTest"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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.KeyguardTransitionInteractor;
|
||||
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.LockscreenToDreamingTransitionViewModel;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
|
||||
@@ -298,6 +299,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
|
||||
@Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
|
||||
@Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
|
||||
@Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
|
||||
@Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
|
||||
|
||||
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||
@Mock private CoroutineDispatcher mMainDispatcher;
|
||||
@@ -522,6 +524,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
|
||||
mDreamingToLockscreenTransitionViewModel,
|
||||
mOccludedToLockscreenTransitionViewModel,
|
||||
mLockscreenToDreamingTransitionViewModel,
|
||||
mGoneToDreamingTransitionViewModel,
|
||||
mLockscreenToOccludedTransitionViewModel,
|
||||
mMainDispatcher,
|
||||
mKeyguardTransitionInteractor,
|
||||
|
||||
Reference in New Issue
Block a user