Merge changes I72c43c93,I254d2130 into tm-qpr-dev am: 5f3bb10877
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21142718 Change-Id: Ibf6e90c18a7ab2c6144c7f6c4d09f1947c97e2c2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -21,6 +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.KeyguardState
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
import com.android.systemui.keyguard.shared.model.TransitionInfo
|
import com.android.systemui.keyguard.shared.model.TransitionInfo
|
||||||
import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
|
import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
|
||||||
@@ -44,6 +45,7 @@ constructor(
|
|||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
listenForDozingToLockscreen()
|
listenForDozingToLockscreen()
|
||||||
|
listenForDozingToGone()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun listenForDozingToLockscreen() {
|
private fun listenForDozingToLockscreen() {
|
||||||
@@ -68,6 +70,28 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun listenForDozingToGone() {
|
||||||
|
scope.launch {
|
||||||
|
keyguardInteractor.biometricUnlockState
|
||||||
|
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
|
||||||
|
.collect { (biometricUnlockState, lastStartedTransition) ->
|
||||||
|
if (
|
||||||
|
lastStartedTransition.to == KeyguardState.DOZING &&
|
||||||
|
isWakeAndUnlock(biometricUnlockState)
|
||||||
|
) {
|
||||||
|
keyguardTransitionRepository.startTransition(
|
||||||
|
TransitionInfo(
|
||||||
|
name,
|
||||||
|
KeyguardState.DOZING,
|
||||||
|
KeyguardState.GONE,
|
||||||
|
getAnimator(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
|
private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
|
||||||
return ValueAnimator().apply {
|
return ValueAnimator().apply {
|
||||||
setInterpolator(Interpolators.LINEAR)
|
setInterpolator(Interpolators.LINEAR)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor
|
|||||||
|
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||||
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.BOUNCER
|
||||||
@@ -31,9 +30,6 @@ 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 kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@@ -104,38 +100,4 @@ constructor(
|
|||||||
/* The last completed [KeyguardState] transition */
|
/* The last completed [KeyguardState] transition */
|
||||||
val finishedKeyguardState: Flow<KeyguardState> =
|
val finishedKeyguardState: Flow<KeyguardState> =
|
||||||
finishedKeyguardTransitionStep.map { step -> step.to }
|
finishedKeyguardTransitionStep.map { step -> step.to }
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions will occur over a [totalDuration] with [TransitionStep]s being emitted in the
|
|
||||||
* range of [0, 1]. View animations should begin and end within a subset of this range. This
|
|
||||||
* function maps the [startTime] and [duration] into [0, 1], when this subset is valid.
|
|
||||||
*/
|
|
||||||
fun transitionStepAnimation(
|
|
||||||
flow: Flow<TransitionStep>,
|
|
||||||
params: AnimationParams,
|
|
||||||
totalDuration: Duration,
|
|
||||||
): Flow<Float> {
|
|
||||||
val start = (params.startTime / totalDuration).toFloat()
|
|
||||||
val chunks = (totalDuration / params.duration).toFloat()
|
|
||||||
var isRunning = false
|
|
||||||
return flow
|
|
||||||
.map { step ->
|
|
||||||
val value = (step.value - start) * chunks
|
|
||||||
if (step.transitionState == STARTED) {
|
|
||||||
// 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 {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter { value -> value >= 0f && value <= 1f }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License
|
|
||||||
*/
|
|
||||||
package com.android.systemui.keyguard.shared.model
|
|
||||||
|
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
|
|
||||||
/** Animation parameters */
|
|
||||||
data class AnimationParams(
|
|
||||||
val startTime: Duration = 0.milliseconds,
|
|
||||||
val duration: Duration,
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import android.view.animation.Interpolator
|
||||||
|
import com.android.systemui.animation.Interpolators.LINEAR
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the given transition params, construct a flow using [createFlow] for the specified portion of
|
||||||
|
* the overall transition.
|
||||||
|
*/
|
||||||
|
class KeyguardTransitionAnimationFlow(
|
||||||
|
private val transitionDuration: Duration,
|
||||||
|
private val transitionFlow: Flow<TransitionStep>,
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted in
|
||||||
|
* the range of [0, 1]. View animations should begin and end within a subset of this range. This
|
||||||
|
* function maps the [startTime] and [duration] into [0, 1], when this subset is valid.
|
||||||
|
*/
|
||||||
|
fun createFlow(
|
||||||
|
duration: Duration,
|
||||||
|
onStep: (Float) -> Float,
|
||||||
|
startTime: Duration = 0.milliseconds,
|
||||||
|
onCancel: (() -> Float)? = null,
|
||||||
|
onFinish: (() -> Float)? = null,
|
||||||
|
interpolator: Interpolator = LINEAR,
|
||||||
|
): Flow<Float> {
|
||||||
|
if (!duration.isPositive()) {
|
||||||
|
throw IllegalArgumentException("duration must be a positive number: $duration")
|
||||||
|
}
|
||||||
|
if ((startTime + duration).compareTo(transitionDuration) > 0) {
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"startTime($startTime) + duration($duration) must be" +
|
||||||
|
" <= transitionDuration($transitionDuration)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val start = (startTime / transitionDuration).toFloat()
|
||||||
|
val chunks = (transitionDuration / duration).toFloat()
|
||||||
|
var isComplete = true
|
||||||
|
|
||||||
|
fun stepToValue(step: TransitionStep): Float? {
|
||||||
|
val value = (step.value - start) * chunks
|
||||||
|
return when (step.transitionState) {
|
||||||
|
// 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
|
||||||
|
STARTED -> {
|
||||||
|
isComplete = false
|
||||||
|
max(0f, min(1f, value))
|
||||||
|
}
|
||||||
|
// Always send a final value of 1. Because of rounding, [value] may never be
|
||||||
|
// exactly 1.
|
||||||
|
RUNNING ->
|
||||||
|
if (isComplete) {
|
||||||
|
null
|
||||||
|
} else if (value >= 1f) {
|
||||||
|
isComplete = true
|
||||||
|
1f
|
||||||
|
} else if (value >= 0f) {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}?.let { onStep(interpolator.getInterpolation(it)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return transitionFlow
|
||||||
|
.map { step ->
|
||||||
|
when (step.transitionState) {
|
||||||
|
STARTED -> stepToValue(step)
|
||||||
|
RUNNING -> stepToValue(step)
|
||||||
|
CANCELED -> onCancel?.invoke()
|
||||||
|
FINISHED -> onFinish?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filterNotNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,15 +21,10 @@ import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
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.ui.KeyguardTransitionAnimationFlow
|
||||||
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.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
|
||||||
@@ -41,39 +36,46 @@ class DreamingToLockscreenTransitionViewModel
|
|||||||
constructor(
|
constructor(
|
||||||
private val interactor: KeyguardTransitionInteractor,
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
) {
|
) {
|
||||||
|
private val transitionAnimation =
|
||||||
|
KeyguardTransitionAnimationFlow(
|
||||||
|
transitionDuration = TO_LOCKSCREEN_DURATION,
|
||||||
|
transitionFlow = interactor.dreamingToLockscreenTransition,
|
||||||
|
)
|
||||||
|
|
||||||
/** Dream overlay y-translation on exit */
|
/** Dream overlay y-translation on exit */
|
||||||
fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
|
fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
|
||||||
return flowForAnimation(DREAM_OVERLAY_TRANSLATION_Y).map { value ->
|
return transitionAnimation.createFlow(
|
||||||
EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx
|
duration = 600.milliseconds,
|
||||||
}
|
onStep = { it * translatePx },
|
||||||
|
interpolator = EMPHASIZED_ACCELERATE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/** Dream overlay views alpha - fade out */
|
/** Dream overlay views alpha - fade out */
|
||||||
val dreamOverlayAlpha: Flow<Float> = flowForAnimation(DREAM_OVERLAY_ALPHA).map { 1f - it }
|
val dreamOverlayAlpha: Flow<Float> =
|
||||||
|
transitionAnimation.createFlow(
|
||||||
|
duration = 250.milliseconds,
|
||||||
|
onStep = { 1f - it },
|
||||||
|
)
|
||||||
|
|
||||||
/** Lockscreen views y-translation */
|
/** Lockscreen views y-translation */
|
||||||
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||||
return merge(
|
return transitionAnimation.createFlow(
|
||||||
flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
|
duration = TO_LOCKSCREEN_DURATION,
|
||||||
-translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
|
onStep = { value -> -translatePx + value * translatePx },
|
||||||
},
|
// Reset on cancel or finish
|
||||||
// On end, reset the translation to 0
|
onFinish = { 0f },
|
||||||
interactor.dreamingToLockscreenTransition
|
onCancel = { 0f },
|
||||||
.filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
|
interpolator = EMPHASIZED_DECELERATE,
|
||||||
.map { 0f }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Lockscreen views alpha */
|
/** Lockscreen views alpha */
|
||||||
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
|
val lockscreenAlpha: Flow<Float> =
|
||||||
|
transitionAnimation.createFlow(
|
||||||
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
|
startTime = 233.milliseconds,
|
||||||
return interactor.transitionStepAnimation(
|
duration = 250.milliseconds,
|
||||||
interactor.dreamingToLockscreenTransition,
|
onStep = { it },
|
||||||
params,
|
|
||||||
totalDuration = TO_LOCKSCREEN_DURATION
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/* Length of time before ending the dream activity, in order to start unoccluding */
|
/* Length of time before ending the dream activity, in order to start unoccluding */
|
||||||
@@ -81,11 +83,5 @@ constructor(
|
|||||||
@JvmField
|
@JvmField
|
||||||
val LOCKSCREEN_ANIMATION_DURATION_MS =
|
val LOCKSCREEN_ANIMATION_DURATION_MS =
|
||||||
(TO_LOCKSCREEN_DURATION - DREAM_ANIMATION_DURATION).inWholeMilliseconds
|
(TO_LOCKSCREEN_DURATION - DREAM_ANIMATION_DURATION).inWholeMilliseconds
|
||||||
|
|
||||||
val DREAM_OVERLAY_TRANSLATION_Y = AnimationParams(duration = 600.milliseconds)
|
|
||||||
val DREAM_OVERLAY_ALPHA = AnimationParams(duration = 250.milliseconds)
|
|
||||||
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
|
|
||||||
val LOCKSCREEN_ALPHA =
|
|
||||||
AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,10 @@ import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
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.FromGoneTransitionInteractor.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.ui.KeyguardTransitionAnimationFlow
|
||||||
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.merge
|
|
||||||
|
|
||||||
/** Breaks down GONE->DREAMING transition into discrete steps for corresponding views to consume. */
|
/** Breaks down GONE->DREAMING transition into discrete steps for corresponding views to consume. */
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@@ -38,32 +33,28 @@ constructor(
|
|||||||
private val interactor: KeyguardTransitionInteractor,
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val transitionAnimation =
|
||||||
|
KeyguardTransitionAnimationFlow(
|
||||||
|
transitionDuration = TO_DREAMING_DURATION,
|
||||||
|
transitionFlow = interactor.goneToDreamingTransition,
|
||||||
|
)
|
||||||
|
|
||||||
/** Lockscreen views y-translation */
|
/** Lockscreen views y-translation */
|
||||||
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||||
return merge(
|
return transitionAnimation.createFlow(
|
||||||
flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
|
duration = 500.milliseconds,
|
||||||
(EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
|
onStep = { it * translatePx },
|
||||||
},
|
// Reset on cancel or finish
|
||||||
// On end, reset the translation to 0
|
onFinish = { 0f },
|
||||||
interactor.goneToDreamingTransition
|
onCancel = { 0f },
|
||||||
.filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
|
interpolator = EMPHASIZED_ACCELERATE,
|
||||||
.map { 0f }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Lockscreen views alpha */
|
/** Lockscreen views alpha */
|
||||||
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
|
val lockscreenAlpha: Flow<Float> =
|
||||||
|
transitionAnimation.createFlow(
|
||||||
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
|
duration = 250.milliseconds,
|
||||||
return interactor.transitionStepAnimation(
|
onStep = { 1f - it },
|
||||||
interactor.goneToDreamingTransition,
|
|
||||||
params,
|
|
||||||
totalDuration = TO_DREAMING_DURATION
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
|
|
||||||
val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,10 @@ import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
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.ui.KeyguardTransitionAnimationFlow
|
||||||
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.merge
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breaks down LOCKSCREEN->DREAMING transition into discrete steps for corresponding views to
|
* Breaks down LOCKSCREEN->DREAMING transition into discrete steps for corresponding views to
|
||||||
@@ -40,35 +35,32 @@ class LockscreenToDreamingTransitionViewModel
|
|||||||
constructor(
|
constructor(
|
||||||
private val interactor: KeyguardTransitionInteractor,
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
) {
|
) {
|
||||||
|
private val transitionAnimation =
|
||||||
|
KeyguardTransitionAnimationFlow(
|
||||||
|
transitionDuration = TO_DREAMING_DURATION,
|
||||||
|
transitionFlow = interactor.lockscreenToDreamingTransition,
|
||||||
|
)
|
||||||
|
|
||||||
/** Lockscreen views y-translation */
|
/** Lockscreen views y-translation */
|
||||||
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||||
return merge(
|
return transitionAnimation.createFlow(
|
||||||
flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
|
duration = 500.milliseconds,
|
||||||
(EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
|
onStep = { it * translatePx },
|
||||||
},
|
// Reset on cancel or finish
|
||||||
// On end, reset the translation to 0
|
onFinish = { 0f },
|
||||||
interactor.lockscreenToDreamingTransition
|
onCancel = { 0f },
|
||||||
.filter { it.transitionState == FINISHED || it.transitionState == CANCELED }
|
interpolator = EMPHASIZED_ACCELERATE,
|
||||||
.map { 0f }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Lockscreen views alpha */
|
/** Lockscreen views alpha */
|
||||||
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }
|
val lockscreenAlpha: Flow<Float> =
|
||||||
|
transitionAnimation.createFlow(
|
||||||
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
|
duration = 250.milliseconds,
|
||||||
return interactor.transitionStepAnimation(
|
onStep = { 1f - it },
|
||||||
interactor.lockscreenToDreamingTransition,
|
|
||||||
params,
|
|
||||||
totalDuration = TO_DREAMING_DURATION
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds
|
@JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds
|
||||||
|
|
||||||
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
|
|
||||||
val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,10 @@ import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
|
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_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.ui.KeyguardTransitionAnimationFlow
|
||||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
|
||||||
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.merge
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breaks down LOCKSCREEN->OCCLUDED transition into discrete steps for corresponding views to
|
* Breaks down LOCKSCREEN->OCCLUDED transition into discrete steps for corresponding views to
|
||||||
@@ -39,33 +35,28 @@ class LockscreenToOccludedTransitionViewModel
|
|||||||
constructor(
|
constructor(
|
||||||
private val interactor: KeyguardTransitionInteractor,
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
) {
|
) {
|
||||||
|
private val transitionAnimation =
|
||||||
|
KeyguardTransitionAnimationFlow(
|
||||||
|
transitionDuration = TO_OCCLUDED_DURATION,
|
||||||
|
transitionFlow = interactor.lockscreenToOccludedTransition,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Lockscreen views alpha */
|
||||||
|
val lockscreenAlpha: Flow<Float> =
|
||||||
|
transitionAnimation.createFlow(
|
||||||
|
duration = 250.milliseconds,
|
||||||
|
onStep = { 1f - it },
|
||||||
|
)
|
||||||
|
|
||||||
/** Lockscreen views y-translation */
|
/** Lockscreen views y-translation */
|
||||||
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||||
return merge(
|
return transitionAnimation.createFlow(
|
||||||
flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
|
duration = TO_OCCLUDED_DURATION,
|
||||||
(EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
|
onStep = { value -> value * translatePx },
|
||||||
},
|
// Reset on cancel or finish
|
||||||
// On end, reset the translation to 0
|
onFinish = { 0f },
|
||||||
interactor.lockscreenToOccludedTransition
|
onCancel = { 0f },
|
||||||
.filter { step -> step.transitionState == TransitionState.FINISHED }
|
interpolator = EMPHASIZED_ACCELERATE,
|
||||||
.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.lockscreenToOccludedTransition,
|
|
||||||
params,
|
|
||||||
totalDuration = TO_OCCLUDED_DURATION
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_OCCLUDED_DURATION)
|
|
||||||
val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
|
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.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.ui.KeyguardTransitionAnimationFlow
|
||||||
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.map
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
|
* Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
|
||||||
@@ -36,28 +35,26 @@ class OccludedToLockscreenTransitionViewModel
|
|||||||
constructor(
|
constructor(
|
||||||
private val interactor: KeyguardTransitionInteractor,
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
) {
|
) {
|
||||||
|
private val transitionAnimation =
|
||||||
|
KeyguardTransitionAnimationFlow(
|
||||||
|
transitionDuration = TO_LOCKSCREEN_DURATION,
|
||||||
|
transitionFlow = interactor.occludedToLockscreenTransition,
|
||||||
|
)
|
||||||
|
|
||||||
/** 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 transitionAnimation.createFlow(
|
||||||
-translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
|
duration = TO_LOCKSCREEN_DURATION,
|
||||||
}
|
onStep = { value -> -translatePx + value * translatePx },
|
||||||
}
|
interpolator = EMPHASIZED_DECELERATE,
|
||||||
|
|
||||||
/** Lockscreen views alpha */
|
|
||||||
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
|
|
||||||
|
|
||||||
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
|
|
||||||
return interactor.transitionStepAnimation(
|
|
||||||
interactor.occludedToLockscreenTransition,
|
|
||||||
params,
|
|
||||||
totalDuration = TO_LOCKSCREEN_DURATION
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
/** Lockscreen views alpha */
|
||||||
@JvmField val LOCKSCREEN_ANIMATION_DURATION_MS = TO_LOCKSCREEN_DURATION.inWholeMilliseconds
|
val lockscreenAlpha: Flow<Float> =
|
||||||
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
|
transitionAnimation.createFlow(
|
||||||
val LOCKSCREEN_ALPHA =
|
startTime = 233.milliseconds,
|
||||||
AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
|
duration = 250.milliseconds,
|
||||||
}
|
onStep = { it },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.android.systemui.animation.Interpolators
|
|||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
|
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
|
||||||
|
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.DozeTransitionModel
|
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
@@ -436,6 +437,43 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
|||||||
coroutineContext.cancelChildren()
|
coroutineContext.cancelChildren()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `DOZING to GONE`() =
|
||||||
|
testScope.runTest {
|
||||||
|
// GIVEN a prior transition has run to DOZING
|
||||||
|
runner.startTransition(
|
||||||
|
testScope,
|
||||||
|
TransitionInfo(
|
||||||
|
ownerName = "",
|
||||||
|
from = KeyguardState.LOCKSCREEN,
|
||||||
|
to = KeyguardState.DOZING,
|
||||||
|
animator =
|
||||||
|
ValueAnimator().apply {
|
||||||
|
duration = 10
|
||||||
|
interpolator = Interpolators.LINEAR
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
runCurrent()
|
||||||
|
reset(mockTransitionRepository)
|
||||||
|
|
||||||
|
// WHEN biometrics succeeds with wake and unlock mode
|
||||||
|
keyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
val info =
|
||||||
|
withArgCaptor<TransitionInfo> {
|
||||||
|
verify(mockTransitionRepository).startTransition(capture())
|
||||||
|
}
|
||||||
|
// THEN a transition to DOZING should occur
|
||||||
|
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
|
||||||
|
assertThat(info.from).isEqualTo(KeyguardState.DOZING)
|
||||||
|
assertThat(info.to).isEqualTo(KeyguardState.GONE)
|
||||||
|
assertThat(info.animator).isNotNull()
|
||||||
|
|
||||||
|
coroutineContext.cancelChildren()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GONE to DOZING`() =
|
fun `GONE to DOZING`() =
|
||||||
testScope.runTest {
|
testScope.runTest {
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||||
|
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.google.common.truth.Truth.assertThat
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
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 KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
|
||||||
|
private lateinit var underTest: KeyguardTransitionAnimationFlow
|
||||||
|
private lateinit var repository: FakeKeyguardTransitionRepository
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
repository = FakeKeyguardTransitionRepository()
|
||||||
|
underTest =
|
||||||
|
KeyguardTransitionAnimationFlow(
|
||||||
|
1000.milliseconds,
|
||||||
|
repository.transitions,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException::class)
|
||||||
|
fun zeroDurationThrowsException() = runTest {
|
||||||
|
val flow = underTest.createFlow(duration = 0.milliseconds, onStep = { it })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException::class)
|
||||||
|
fun startTimePlusDurationGreaterThanTransitionDurationThrowsException() = runTest {
|
||||||
|
val flow =
|
||||||
|
underTest.createFlow(
|
||||||
|
startTime = 300.milliseconds,
|
||||||
|
duration = 800.milliseconds,
|
||||||
|
onStep = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onFinishRunsWhenSpecified() = runTest {
|
||||||
|
val flow =
|
||||||
|
underTest.createFlow(
|
||||||
|
duration = 100.milliseconds,
|
||||||
|
onStep = { it },
|
||||||
|
onFinish = { 10f },
|
||||||
|
)
|
||||||
|
var animationValues = collectLastValue(flow)
|
||||||
|
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
|
||||||
|
assertThat(animationValues()).isEqualTo(10f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onCancelRunsWhenSpecified() = runTest {
|
||||||
|
val flow =
|
||||||
|
underTest.createFlow(
|
||||||
|
duration = 100.milliseconds,
|
||||||
|
onStep = { it },
|
||||||
|
onCancel = { 100f },
|
||||||
|
)
|
||||||
|
var animationValues = collectLastValue(flow)
|
||||||
|
repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
|
||||||
|
assertThat(animationValues()).isEqualTo(100f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun usesStartTime() = runTest {
|
||||||
|
val flow =
|
||||||
|
underTest.createFlow(
|
||||||
|
startTime = 500.milliseconds,
|
||||||
|
duration = 500.milliseconds,
|
||||||
|
onStep = { it },
|
||||||
|
)
|
||||||
|
var animationValues = collectLastValue(flow)
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
|
assertThat(animationValues()).isEqualTo(0f)
|
||||||
|
|
||||||
|
// Should not emit a value
|
||||||
|
repository.sendTransitionStep(step(0.1f, TransitionState.RUNNING))
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 0f)
|
||||||
|
repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 0.2f)
|
||||||
|
repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 0.6f)
|
||||||
|
repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun usesInterpolator() = runTest {
|
||||||
|
val flow =
|
||||||
|
underTest.createFlow(
|
||||||
|
duration = 1000.milliseconds,
|
||||||
|
interpolator = EMPHASIZED_ACCELERATE,
|
||||||
|
onStep = { it },
|
||||||
|
)
|
||||||
|
var animationValues = collectLastValue(flow)
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
|
assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0f))
|
||||||
|
repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0.5f))
|
||||||
|
repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0.6f))
|
||||||
|
repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0.8f))
|
||||||
|
repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(1f))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun usesOnStepToDoubleValue() = runTest {
|
||||||
|
val flow =
|
||||||
|
underTest.createFlow(
|
||||||
|
duration = 1000.milliseconds,
|
||||||
|
onStep = { it * 2 },
|
||||||
|
)
|
||||||
|
var animationValues = collectLastValue(flow)
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
|
assertFloat(animationValues(), 0f)
|
||||||
|
repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 0.6f)
|
||||||
|
repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 1.2f)
|
||||||
|
repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 1.6f)
|
||||||
|
repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
|
||||||
|
assertFloat(animationValues(), 2f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertFloat(actual: Float?, expected: Float) {
|
||||||
|
assertThat(actual!!).isWithin(0.01f).of(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun step(
|
||||||
|
value: Float,
|
||||||
|
state: TransitionState = TransitionState.RUNNING
|
||||||
|
): TransitionStep {
|
||||||
|
return TransitionStep(
|
||||||
|
from = KeyguardState.GONE,
|
||||||
|
to = KeyguardState.DREAMING,
|
||||||
|
value = value,
|
||||||
|
transitionState = state,
|
||||||
|
ownerName = "GoneToDreamingTransitionViewModelTest"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,19 +18,13 @@ package com.android.systemui.keyguard.ui.viewmodel
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
|
|
||||||
import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
|
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||||
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.KeyguardState
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
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.Companion.DREAM_OVERLAY_ALPHA
|
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_OVERLAY_TRANSLATION_Y
|
import com.google.common.collect.Range
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.LOCKSCREEN_ALPHA
|
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
@@ -46,6 +40,7 @@ import org.junit.runners.JUnit4
|
|||||||
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
||||||
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
|
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
|
||||||
private lateinit var repository: FakeKeyguardTransitionRepository
|
private lateinit var repository: FakeKeyguardTransitionRepository
|
||||||
|
private lateinit var transitionAnimation: KeyguardTransitionAnimationFlow
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -63,32 +58,18 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job =
|
val job =
|
||||||
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
|
repository.sendTransitionStep(step(0.6f))
|
||||||
|
// ...up to here
|
||||||
|
repository.sendTransitionStep(step(0.8f))
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
// Only 3 values should be present, since the dream overlay runs for a small fraction
|
assertThat(values.size).isEqualTo(5)
|
||||||
// of the overall animation time
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
|
||||||
assertThat(values.size).isEqualTo(3)
|
|
||||||
assertThat(values[0])
|
|
||||||
.isEqualTo(
|
|
||||||
EMPHASIZED_ACCELERATE.getInterpolation(
|
|
||||||
animValue(0f, DREAM_OVERLAY_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[1])
|
|
||||||
.isEqualTo(
|
|
||||||
EMPHASIZED_ACCELERATE.getInterpolation(
|
|
||||||
animValue(0.3f, DREAM_OVERLAY_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[2])
|
|
||||||
.isEqualTo(
|
|
||||||
EMPHASIZED_ACCELERATE.getInterpolation(
|
|
||||||
animValue(0.5f, DREAM_OVERLAY_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -100,16 +81,18 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
|||||||
|
|
||||||
val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
|
val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.1f))
|
repository.sendTransitionStep(step(0.1f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
|
// ...up to here
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
// Only two values should be present, since the dream overlay runs for a small fraction
|
// Only two values should be present, since the dream overlay runs for a small fraction
|
||||||
// of the overall animation time
|
// of the overall animation time
|
||||||
assertThat(values.size).isEqualTo(2)
|
assertThat(values.size).isEqualTo(4)
|
||||||
assertThat(values[0]).isEqualTo(1f - animValue(0f, DREAM_OVERLAY_ALPHA))
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
|
||||||
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, DREAM_OVERLAY_ALPHA))
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -121,19 +104,15 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
|||||||
|
|
||||||
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.1f))
|
repository.sendTransitionStep(step(0.1f))
|
||||||
// Should start running here...
|
|
||||||
repository.sendTransitionStep(step(0.2f))
|
repository.sendTransitionStep(step(0.2f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
// Only two values should be present, since the dream overlay runs for a small fraction
|
assertThat(values.size).isEqualTo(4)
|
||||||
// of the overall animation time
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
|
||||||
assertThat(values.size).isEqualTo(2)
|
|
||||||
assertThat(values[0]).isEqualTo(animValue(0.2f, LOCKSCREEN_ALPHA))
|
|
||||||
assertThat(values[1]).isEqualTo(animValue(0.3f, LOCKSCREEN_ALPHA))
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -147,58 +126,27 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job =
|
val job =
|
||||||
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
assertThat(values.size).isEqualTo(4)
|
assertThat(values.size).isEqualTo(5)
|
||||||
assertThat(values[0])
|
values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(0f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[1])
|
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[2])
|
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[3])
|
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(1f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animValue(stepValue: Float, params: AnimationParams): Float {
|
private fun step(
|
||||||
val totalDuration = TO_LOCKSCREEN_DURATION
|
value: Float,
|
||||||
val startValue = (params.startTime / totalDuration).toFloat()
|
state: TransitionState = TransitionState.RUNNING
|
||||||
|
): TransitionStep {
|
||||||
val multiplier = (totalDuration / params.duration).toFloat()
|
|
||||||
return (stepValue - startValue) * multiplier
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun step(value: Float): TransitionStep {
|
|
||||||
return TransitionStep(
|
return TransitionStep(
|
||||||
from = KeyguardState.DREAMING,
|
from = KeyguardState.DREAMING,
|
||||||
to = KeyguardState.LOCKSCREEN,
|
to = KeyguardState.LOCKSCREEN,
|
||||||
value = value,
|
value = value,
|
||||||
transitionState = TransitionState.RUNNING,
|
transitionState = state,
|
||||||
ownerName = "DreamingToLockscreenTransitionViewModelTest"
|
ownerName = "DreamingToLockscreenTransitionViewModelTest"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
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.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.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.KeyguardState
|
||||||
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.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
|
import com.google.common.collect.Range
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
@@ -59,20 +55,18 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
// Should start running here...
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.1f))
|
repository.sendTransitionStep(step(0.1f))
|
||||||
repository.sendTransitionStep(step(0.2f))
|
repository.sendTransitionStep(step(0.2f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
|
// ...up to here
|
||||||
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(5)
|
||||||
assertThat(values.size).isEqualTo(3)
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
|
||||||
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()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -87,45 +81,19 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
|
|||||||
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
// Should start running here...
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(1f))
|
|
||||||
// And a final reset event on CANCEL
|
// And a final reset event on CANCEL
|
||||||
repository.sendTransitionStep(step(0.8f, TransitionState.CANCELED))
|
repository.sendTransitionStep(step(0.8f, TransitionState.CANCELED))
|
||||||
|
|
||||||
assertThat(values.size).isEqualTo(4)
|
assertThat(values.size).isEqualTo(5)
|
||||||
assertThat(values[0])
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
|
||||||
.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()
|
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(
|
private fun step(
|
||||||
value: Float,
|
value: Float,
|
||||||
state: TransitionState = TransitionState.RUNNING
|
state: TransitionState = TransitionState.RUNNING
|
||||||
|
|||||||
@@ -18,16 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
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.data.repository.FakeKeyguardTransitionRepository
|
||||||
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.KeyguardState
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
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.LockscreenToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
|
import com.google.common.collect.Range
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
@@ -59,19 +55,18 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
// Should start running here...
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.1f))
|
repository.sendTransitionStep(step(0.1f))
|
||||||
repository.sendTransitionStep(step(0.2f))
|
repository.sendTransitionStep(step(0.2f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
|
// ...up to here
|
||||||
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 of the overall animation time
|
// fraction of the overall animation time
|
||||||
assertThat(values.size).isEqualTo(3)
|
assertThat(values.size).isEqualTo(5)
|
||||||
assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
|
||||||
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
|
|
||||||
assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA))
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -85,47 +80,22 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job =
|
val job =
|
||||||
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
// Should start running here...
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
// And a final reset event on FINISHED
|
// And a final reset event on FINISHED
|
||||||
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
|
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
|
||||||
|
|
||||||
assertThat(values.size).isEqualTo(4)
|
assertThat(values.size).isEqualTo(6)
|
||||||
assertThat(values[0])
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
|
||||||
.isEqualTo(
|
// Validate finished value
|
||||||
EMPHASIZED_ACCELERATE.getInterpolation(
|
assertThat(values[5]).isEqualTo(0f)
|
||||||
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()
|
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(
|
private fun step(
|
||||||
value: Float,
|
value: Float,
|
||||||
state: TransitionState = TransitionState.RUNNING
|
state: TransitionState = TransitionState.RUNNING
|
||||||
|
|||||||
@@ -18,16 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
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.data.repository.FakeKeyguardTransitionRepository
|
||||||
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_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.KeyguardState
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
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.LockscreenToOccludedTransitionViewModel.Companion.LOCKSCREEN_ALPHA
|
import com.google.common.collect.Range
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
@@ -59,19 +55,18 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
// Should start running here...
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.1f))
|
repository.sendTransitionStep(step(0.1f))
|
||||||
repository.sendTransitionStep(step(0.4f))
|
repository.sendTransitionStep(step(0.4f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(0.7f))
|
repository.sendTransitionStep(step(0.7f))
|
||||||
|
// ...up to here
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
// Only 3 values should be present, since the dream overlay runs for a small fraction
|
// Only 3 values should be present, since the dream overlay runs for a small fraction
|
||||||
// of the overall animation time
|
// of the overall animation time
|
||||||
assertThat(values.size).isEqualTo(3)
|
assertThat(values.size).isEqualTo(5)
|
||||||
assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
|
||||||
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
|
|
||||||
assertThat(values[2]).isEqualTo(1f - animValue(0.4f, LOCKSCREEN_ALPHA))
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -86,54 +81,51 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
|
|||||||
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
// Should start running here...
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
// ...up to here
|
// ...up to here
|
||||||
|
|
||||||
assertThat(values.size).isEqualTo(4)
|
assertThat(values.size).isEqualTo(5)
|
||||||
assertThat(values[0])
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
|
||||||
.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(
|
|
||||||
EMPHASIZED_ACCELERATE.getInterpolation(
|
|
||||||
animValue(1f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animValue(stepValue: Float, params: AnimationParams): Float {
|
@Test
|
||||||
val totalDuration = TO_OCCLUDED_DURATION
|
fun lockscreenTranslationYIsCanceled() =
|
||||||
val startValue = (params.startTime / totalDuration).toFloat()
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
|
val values = mutableListOf<Float>()
|
||||||
|
|
||||||
val multiplier = (totalDuration / params.duration).toFloat()
|
val pixels = 100
|
||||||
return (stepValue - startValue) * multiplier
|
val job =
|
||||||
}
|
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
private fun step(value: Float): TransitionStep {
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
|
repository.sendTransitionStep(step(0f))
|
||||||
|
repository.sendTransitionStep(step(0.3f))
|
||||||
|
repository.sendTransitionStep(step(0.3f, TransitionState.CANCELED))
|
||||||
|
|
||||||
|
assertThat(values.size).isEqualTo(4)
|
||||||
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
|
||||||
|
|
||||||
|
// Cancel will reset the translation
|
||||||
|
assertThat(values[3]).isEqualTo(0)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun step(
|
||||||
|
value: Float,
|
||||||
|
state: TransitionState = TransitionState.RUNNING,
|
||||||
|
): TransitionStep {
|
||||||
return TransitionStep(
|
return TransitionStep(
|
||||||
from = KeyguardState.LOCKSCREEN,
|
from = KeyguardState.LOCKSCREEN,
|
||||||
to = KeyguardState.OCCLUDED,
|
to = KeyguardState.OCCLUDED,
|
||||||
value = value,
|
value = value,
|
||||||
transitionState = TransitionState.RUNNING,
|
transitionState = state,
|
||||||
ownerName = "LockscreenToOccludedTransitionViewModelTest"
|
ownerName = "LockscreenToOccludedTransitionViewModelTest"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
|
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||||
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.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.KeyguardState
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
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.OccludedToLockscreenTransitionViewModel.Companion.LOCKSCREEN_ALPHA
|
import com.google.common.collect.Range
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
@@ -58,21 +54,19 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
|||||||
|
|
||||||
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
|
repository.sendTransitionStep(step(0.1f))
|
||||||
// Should start running here...
|
// Should start running here...
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.4f))
|
repository.sendTransitionStep(step(0.4f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
// ...up to here
|
|
||||||
repository.sendTransitionStep(step(0.6f))
|
repository.sendTransitionStep(step(0.6f))
|
||||||
|
// ...up to here
|
||||||
|
repository.sendTransitionStep(step(0.8f))
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
// Only two values should be present, since the dream overlay runs for a small fraction
|
assertThat(values.size).isEqualTo(5)
|
||||||
// of the overall animation time
|
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
|
||||||
assertThat(values.size).isEqualTo(3)
|
|
||||||
assertThat(values[0]).isEqualTo(animValue(0.3f, LOCKSCREEN_ALPHA))
|
|
||||||
assertThat(values[1]).isEqualTo(animValue(0.4f, LOCKSCREEN_ALPHA))
|
|
||||||
assertThat(values[2]).isEqualTo(animValue(0.5f, LOCKSCREEN_ALPHA))
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -86,58 +80,27 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
|||||||
val job =
|
val job =
|
||||||
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
|
||||||
repository.sendTransitionStep(step(0f))
|
repository.sendTransitionStep(step(0f))
|
||||||
repository.sendTransitionStep(step(0.3f))
|
repository.sendTransitionStep(step(0.3f))
|
||||||
repository.sendTransitionStep(step(0.5f))
|
repository.sendTransitionStep(step(0.5f))
|
||||||
repository.sendTransitionStep(step(1f))
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
assertThat(values.size).isEqualTo(4)
|
assertThat(values.size).isEqualTo(5)
|
||||||
assertThat(values[0])
|
values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(0f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[1])
|
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[2])
|
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
assertThat(values[3])
|
|
||||||
.isEqualTo(
|
|
||||||
-pixels +
|
|
||||||
EMPHASIZED_DECELERATE.getInterpolation(
|
|
||||||
animValue(1f, LOCKSCREEN_TRANSLATION_Y)
|
|
||||||
) * pixels
|
|
||||||
)
|
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animValue(stepValue: Float, params: AnimationParams): Float {
|
private fun step(
|
||||||
val totalDuration = TO_LOCKSCREEN_DURATION
|
value: Float,
|
||||||
val startValue = (params.startTime / totalDuration).toFloat()
|
state: TransitionState = TransitionState.RUNNING
|
||||||
|
): TransitionStep {
|
||||||
val multiplier = (totalDuration / params.duration).toFloat()
|
|
||||||
return (stepValue - startValue) * multiplier
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun step(value: Float): TransitionStep {
|
|
||||||
return TransitionStep(
|
return TransitionStep(
|
||||||
from = KeyguardState.OCCLUDED,
|
from = KeyguardState.OCCLUDED,
|
||||||
to = KeyguardState.LOCKSCREEN,
|
to = KeyguardState.LOCKSCREEN,
|
||||||
value = value,
|
value = value,
|
||||||
transitionState = TransitionState.RUNNING,
|
transitionState = state,
|
||||||
ownerName = "OccludedToLockscreenTransitionViewModelTest"
|
ownerName = "OccludedToLockscreenTransitionViewModelTest"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user