[DO NOT MERGE] Coordinated DREAMING->LOCKSCREEN animation
Part 1: Update dream side. Shift to utilize KeyguardTransitionRepository as the source for all motion between states. DreamingToLockscreenTransitionViewModel contains all logic that subdivides the total animation time into flowables that emit events during only their proper subsection. This means one animator now runs the view animations across all spaces. As part of this, add DreamCallbackController as a data source for KeyguardRepository. The dream activity will signal the transition to begin via onWakeUp(), which will start the view motions, then run the unocclude transition, and then finally the lockscreen view motions. Part 2 coming next... lockscreen tweaks Test: atest KeyguardRepositoryImplTest DreamOverlayContainerViewControllerTest DreamOverlayAnimationsControllerTest DreamingToLockscreenTransitionViewModelTest DreamOverlayServiceTest Bug: 260631205 Change-Id: I9bcf877927d1e92e8c751d0b83d2dd70f15912f1
This commit is contained in:
@@ -743,27 +743,6 @@
|
|||||||
<!-- How long in milliseconds before full burn-in protection is achieved. -->
|
<!-- How long in milliseconds before full burn-in protection is achieved. -->
|
||||||
<integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer>
|
<integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer>
|
||||||
|
|
||||||
<!-- The duration in milliseconds of the y-translation animation when waking up from
|
|
||||||
the dream -->
|
|
||||||
<integer name="config_dreamOverlayOutTranslationYDurationMs">333</integer>
|
|
||||||
<!-- The delay in milliseconds of the y-translation animation when waking up from
|
|
||||||
the dream for the complications at the bottom of the screen -->
|
|
||||||
<integer name="config_dreamOverlayOutTranslationYDelayBottomMs">33</integer>
|
|
||||||
<!-- The delay in milliseconds of the y-translation animation when waking up from
|
|
||||||
the dream for the complications at the top of the screen -->
|
|
||||||
<integer name="config_dreamOverlayOutTranslationYDelayTopMs">117</integer>
|
|
||||||
<!-- The duration in milliseconds of the alpha animation when waking up from the dream -->
|
|
||||||
<integer name="config_dreamOverlayOutAlphaDurationMs">200</integer>
|
|
||||||
<!-- The delay in milliseconds of the alpha animation when waking up from the dream for the
|
|
||||||
complications at the top of the screen -->
|
|
||||||
<integer name="config_dreamOverlayOutAlphaDelayTopMs">217</integer>
|
|
||||||
<!-- The delay in milliseconds of the alpha animation when waking up from the dream for the
|
|
||||||
complications at the bottom of the screen -->
|
|
||||||
<integer name="config_dreamOverlayOutAlphaDelayBottomMs">133</integer>
|
|
||||||
<!-- The duration in milliseconds of the blur animation when waking up from
|
|
||||||
the dream -->
|
|
||||||
<integer name="config_dreamOverlayOutBlurDurationMs">250</integer>
|
|
||||||
|
|
||||||
<integer name="complicationFadeOutMs">500</integer>
|
<integer name="complicationFadeOutMs">500</integer>
|
||||||
|
|
||||||
<integer name="complicationFadeInMs">500</integer>
|
<integer name="complicationFadeInMs">500</integer>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.dreams
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.statusbar.policy.CallbackController
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** Dream-related callback information */
|
||||||
|
@SysUISingleton
|
||||||
|
class DreamCallbackController @Inject constructor() :
|
||||||
|
CallbackController<DreamCallbackController.DreamCallback> {
|
||||||
|
|
||||||
|
private val callbacks = mutableSetOf<DreamCallbackController.DreamCallback>()
|
||||||
|
|
||||||
|
override fun addCallback(callback: DreamCallbackController.DreamCallback) {
|
||||||
|
callbacks.add(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeCallback(callback: DreamCallbackController.DreamCallback) {
|
||||||
|
callbacks.remove(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onWakeUp() {
|
||||||
|
callbacks.forEach { it.onWakeUp() }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DreamCallback {
|
||||||
|
fun onWakeUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,9 @@ import android.animation.ValueAnimator
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.Interpolator
|
import android.view.animation.Interpolator
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.android.systemui.R
|
||||||
import com.android.systemui.animation.Interpolators
|
import com.android.systemui.animation.Interpolators
|
||||||
import com.android.systemui.dreams.complication.ComplicationHostViewController
|
import com.android.systemui.dreams.complication.ComplicationHostViewController
|
||||||
import com.android.systemui.dreams.complication.ComplicationLayoutParams
|
import com.android.systemui.dreams.complication.ComplicationLayoutParams
|
||||||
@@ -29,10 +32,20 @@ import com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITIO
|
|||||||
import com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_TOP
|
import com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_TOP
|
||||||
import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position
|
import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position
|
||||||
import com.android.systemui.dreams.dagger.DreamOverlayModule
|
import com.android.systemui.dreams.dagger.DreamOverlayModule
|
||||||
|
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
|
||||||
|
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_ANIMATION_DURATION
|
||||||
|
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||||
import com.android.systemui.statusbar.BlurUtils
|
import com.android.systemui.statusbar.BlurUtils
|
||||||
import com.android.systemui.statusbar.CrossFadeHelper
|
import com.android.systemui.statusbar.CrossFadeHelper
|
||||||
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
|
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
|
||||||
|
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Named
|
import javax.inject.Named
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/** Controller for dream overlay animations. */
|
/** Controller for dream overlay animations. */
|
||||||
class DreamOverlayAnimationsController
|
class DreamOverlayAnimationsController
|
||||||
@@ -43,6 +56,8 @@ constructor(
|
|||||||
private val mStatusBarViewController: DreamOverlayStatusBarViewController,
|
private val mStatusBarViewController: DreamOverlayStatusBarViewController,
|
||||||
private val mOverlayStateController: DreamOverlayStateController,
|
private val mOverlayStateController: DreamOverlayStateController,
|
||||||
@Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
|
@Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
|
||||||
|
private val transitionViewModel: DreamingToLockscreenTransitionViewModel,
|
||||||
|
private val configController: ConfigurationController,
|
||||||
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
|
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
|
||||||
private val mDreamInBlurAnimDurationMs: Long,
|
private val mDreamInBlurAnimDurationMs: Long,
|
||||||
@Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
|
@Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
|
||||||
@@ -51,22 +66,10 @@ constructor(
|
|||||||
private val mDreamInTranslationYDistance: Int,
|
private val mDreamInTranslationYDistance: Int,
|
||||||
@Named(DreamOverlayModule.DREAM_IN_TRANSLATION_Y_DURATION)
|
@Named(DreamOverlayModule.DREAM_IN_TRANSLATION_Y_DURATION)
|
||||||
private val mDreamInTranslationYDurationMs: Long,
|
private val mDreamInTranslationYDurationMs: Long,
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DISTANCE)
|
|
||||||
private val mDreamOutTranslationYDistance: Int,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DURATION)
|
|
||||||
private val mDreamOutTranslationYDurationMs: Long,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM)
|
|
||||||
private val mDreamOutTranslationYDelayBottomMs: Long,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DELAY_TOP)
|
|
||||||
private val mDreamOutTranslationYDelayTopMs: Long,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_ALPHA_DURATION) private val mDreamOutAlphaDurationMs: Long,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_ALPHA_DELAY_BOTTOM)
|
|
||||||
private val mDreamOutAlphaDelayBottomMs: Long,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_ALPHA_DELAY_TOP) private val mDreamOutAlphaDelayTopMs: Long,
|
|
||||||
@Named(DreamOverlayModule.DREAM_OUT_BLUR_DURATION) private val mDreamOutBlurDurationMs: Long
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private var mAnimator: Animator? = null
|
private var mAnimator: Animator? = null
|
||||||
|
private lateinit var view: View
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the current alphas at the various positions. This is so that we may resume an animation
|
* Store the current alphas at the various positions. This is so that we may resume an animation
|
||||||
@@ -76,9 +79,63 @@ constructor(
|
|||||||
|
|
||||||
private var mCurrentBlurRadius: Float = 0f
|
private var mCurrentBlurRadius: Float = 0f
|
||||||
|
|
||||||
|
fun init(view: View) {
|
||||||
|
this.view = view
|
||||||
|
|
||||||
|
view.repeatWhenAttached {
|
||||||
|
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
|
||||||
|
val configCallback =
|
||||||
|
object : ConfigurationListener {
|
||||||
|
override fun onDensityOrFontScaleChanged() {
|
||||||
|
configurationBasedDimensions.value = loadFromResources(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configController.addCallback(configCallback)
|
||||||
|
|
||||||
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
/* Translation animations, when moving from DREAMING->LOCKSCREEN state */
|
||||||
|
launch {
|
||||||
|
configurationBasedDimensions
|
||||||
|
.flatMapLatest {
|
||||||
|
transitionViewModel.dreamOverlayTranslationY(it.translationYPx)
|
||||||
|
}
|
||||||
|
.collect { px ->
|
||||||
|
setElementsTranslationYAtPosition(
|
||||||
|
px,
|
||||||
|
ComplicationLayoutParams.POSITION_TOP
|
||||||
|
)
|
||||||
|
setElementsTranslationYAtPosition(
|
||||||
|
px,
|
||||||
|
ComplicationLayoutParams.POSITION_BOTTOM
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alpha animations, when moving from DREAMING->LOCKSCREEN state */
|
||||||
|
launch {
|
||||||
|
transitionViewModel.dreamOverlayAlpha.collect { alpha ->
|
||||||
|
setElementsAlphaAtPosition(
|
||||||
|
alpha = alpha,
|
||||||
|
position = ComplicationLayoutParams.POSITION_TOP,
|
||||||
|
fadingOut = true,
|
||||||
|
)
|
||||||
|
setElementsAlphaAtPosition(
|
||||||
|
alpha = alpha,
|
||||||
|
position = ComplicationLayoutParams.POSITION_BOTTOM,
|
||||||
|
fadingOut = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configController.removeCallback(configCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Starts the dream content and dream overlay entry animations. */
|
/** Starts the dream content and dream overlay entry animations. */
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun startEntryAnimations(view: View, animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
|
fun startEntryAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
|
||||||
cancelAnimations()
|
cancelAnimations()
|
||||||
|
|
||||||
mAnimator =
|
mAnimator =
|
||||||
@@ -113,73 +170,9 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Starts the dream content and dream overlay exit animations. */
|
/** Starts the dream content and dream overlay exit animations. */
|
||||||
@JvmOverloads
|
fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
|
||||||
fun startExitAnimations(
|
|
||||||
view: View,
|
|
||||||
doneCallback: () -> Unit,
|
|
||||||
animatorBuilder: () -> AnimatorSet = { AnimatorSet() }
|
|
||||||
) {
|
|
||||||
cancelAnimations()
|
cancelAnimations()
|
||||||
|
executor.executeDelayed(doneCallback, DREAM_ANIMATION_DURATION.inWholeMilliseconds)
|
||||||
mAnimator =
|
|
||||||
animatorBuilder().apply {
|
|
||||||
playTogether(
|
|
||||||
blurAnimator(
|
|
||||||
view = view,
|
|
||||||
// Start the blurring wherever the entry animation ended, in
|
|
||||||
// case it was cancelled early.
|
|
||||||
fromBlurRadius = mCurrentBlurRadius,
|
|
||||||
toBlurRadius = mDreamBlurRadius.toFloat(),
|
|
||||||
durationMs = mDreamOutBlurDurationMs,
|
|
||||||
interpolator = Interpolators.EMPHASIZED_ACCELERATE
|
|
||||||
),
|
|
||||||
translationYAnimator(
|
|
||||||
from = 0f,
|
|
||||||
to = mDreamOutTranslationYDistance.toFloat(),
|
|
||||||
durationMs = mDreamOutTranslationYDurationMs,
|
|
||||||
delayMs = mDreamOutTranslationYDelayBottomMs,
|
|
||||||
positions = POSITION_BOTTOM,
|
|
||||||
interpolator = Interpolators.EMPHASIZED_ACCELERATE
|
|
||||||
),
|
|
||||||
translationYAnimator(
|
|
||||||
from = 0f,
|
|
||||||
to = mDreamOutTranslationYDistance.toFloat(),
|
|
||||||
durationMs = mDreamOutTranslationYDurationMs,
|
|
||||||
delayMs = mDreamOutTranslationYDelayTopMs,
|
|
||||||
positions = POSITION_TOP,
|
|
||||||
interpolator = Interpolators.EMPHASIZED_ACCELERATE
|
|
||||||
),
|
|
||||||
alphaAnimator(
|
|
||||||
from =
|
|
||||||
mCurrentAlphaAtPosition.getOrDefault(
|
|
||||||
key = POSITION_BOTTOM,
|
|
||||||
defaultValue = 1f
|
|
||||||
),
|
|
||||||
to = 0f,
|
|
||||||
durationMs = mDreamOutAlphaDurationMs,
|
|
||||||
delayMs = mDreamOutAlphaDelayBottomMs,
|
|
||||||
positions = POSITION_BOTTOM
|
|
||||||
),
|
|
||||||
alphaAnimator(
|
|
||||||
from =
|
|
||||||
mCurrentAlphaAtPosition.getOrDefault(
|
|
||||||
key = POSITION_TOP,
|
|
||||||
defaultValue = 1f
|
|
||||||
),
|
|
||||||
to = 0f,
|
|
||||||
durationMs = mDreamOutAlphaDurationMs,
|
|
||||||
delayMs = mDreamOutAlphaDelayTopMs,
|
|
||||||
positions = POSITION_TOP
|
|
||||||
)
|
|
||||||
)
|
|
||||||
doOnEnd {
|
|
||||||
mAnimator = null
|
|
||||||
mOverlayStateController.setExitAnimationsRunning(false)
|
|
||||||
doneCallback()
|
|
||||||
}
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
mOverlayStateController.setExitAnimationsRunning(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancels the dream content and dream overlay animations, if they're currently running. */
|
/** Cancels the dream content and dream overlay animations, if they're currently running. */
|
||||||
@@ -288,4 +281,15 @@ constructor(
|
|||||||
mStatusBarViewController.setTranslationY(translationY)
|
mStatusBarViewController.setTranslationY(translationY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadFromResources(view: View): ConfigurationBasedDimensions {
|
||||||
|
return ConfigurationBasedDimensions(
|
||||||
|
translationYPx =
|
||||||
|
view.resources.getDimensionPixelSize(R.dimen.dream_overlay_exit_y_offset),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class ConfigurationBasedDimensions(
|
||||||
|
val translationYPx: Int,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ import com.android.systemui.statusbar.BlurUtils;
|
|||||||
import com.android.systemui.statusbar.phone.KeyguardBouncer;
|
import com.android.systemui.statusbar.phone.KeyguardBouncer;
|
||||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||||
import com.android.systemui.util.ViewController;
|
import com.android.systemui.util.ViewController;
|
||||||
|
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -170,6 +170,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
|||||||
protected void onInit() {
|
protected void onInit() {
|
||||||
mStatusBarViewController.init();
|
mStatusBarViewController.init();
|
||||||
mComplicationHostViewController.init();
|
mComplicationHostViewController.init();
|
||||||
|
mDreamOverlayAnimationsController.init(mView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -184,7 +185,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
|||||||
|
|
||||||
// Start dream entry animations. Skip animations for low light clock.
|
// Start dream entry animations. Skip animations for low light clock.
|
||||||
if (!mStateController.isLowLightActive()) {
|
if (!mStateController.isLowLightActive()) {
|
||||||
mDreamOverlayAnimationsController.startEntryAnimations(mView);
|
mDreamOverlayAnimationsController.startEntryAnimations();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,10 +262,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
|||||||
* @param onAnimationEnd Callback to trigger once animations are finished.
|
* @param onAnimationEnd Callback to trigger once animations are finished.
|
||||||
* @param callbackExecutor Executor to execute the callback on.
|
* @param callbackExecutor Executor to execute the callback on.
|
||||||
*/
|
*/
|
||||||
public void wakeUp(@NonNull Runnable onAnimationEnd, @NonNull Executor callbackExecutor) {
|
public void wakeUp(@NonNull Runnable onAnimationEnd,
|
||||||
mDreamOverlayAnimationsController.startExitAnimations(mView, () -> {
|
@NonNull DelayableExecutor callbackExecutor) {
|
||||||
callbackExecutor.execute(onAnimationEnd);
|
mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ import com.android.systemui.dagger.qualifiers.Main;
|
|||||||
import com.android.systemui.dreams.complication.Complication;
|
import com.android.systemui.dreams.complication.Complication;
|
||||||
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
|
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
|
||||||
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
|
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
|
||||||
|
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -61,10 +60,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
|
|||||||
// The Context is used to construct the hosting constraint layout and child overlay views.
|
// The Context is used to construct the hosting constraint layout and child overlay views.
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
// The Executor ensures actions and ui updates happen on the same thread.
|
// The Executor ensures actions and ui updates happen on the same thread.
|
||||||
private final Executor mExecutor;
|
private final DelayableExecutor mExecutor;
|
||||||
// A controller for the dream overlay container view (which contains both the status bar and the
|
// A controller for the dream overlay container view (which contains both the status bar and the
|
||||||
// content area).
|
// content area).
|
||||||
private DreamOverlayContainerViewController mDreamOverlayContainerViewController;
|
private DreamOverlayContainerViewController mDreamOverlayContainerViewController;
|
||||||
|
private final DreamCallbackController mDreamCallbackController;
|
||||||
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final ComponentName mLowLightDreamComponent;
|
private final ComponentName mLowLightDreamComponent;
|
||||||
@@ -126,14 +126,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
|
|||||||
@Inject
|
@Inject
|
||||||
public DreamOverlayService(
|
public DreamOverlayService(
|
||||||
Context context,
|
Context context,
|
||||||
@Main Executor executor,
|
@Main DelayableExecutor executor,
|
||||||
WindowManager windowManager,
|
WindowManager windowManager,
|
||||||
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
|
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
|
||||||
DreamOverlayStateController stateController,
|
DreamOverlayStateController stateController,
|
||||||
KeyguardUpdateMonitor keyguardUpdateMonitor,
|
KeyguardUpdateMonitor keyguardUpdateMonitor,
|
||||||
UiEventLogger uiEventLogger,
|
UiEventLogger uiEventLogger,
|
||||||
@Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
|
@Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
|
||||||
ComponentName lowLightDreamComponent) {
|
ComponentName lowLightDreamComponent,
|
||||||
|
DreamCallbackController dreamCallbackController) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mExecutor = executor;
|
mExecutor = executor;
|
||||||
mWindowManager = windowManager;
|
mWindowManager = windowManager;
|
||||||
@@ -142,6 +143,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
|
|||||||
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
|
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
|
||||||
mStateController = stateController;
|
mStateController = stateController;
|
||||||
mUiEventLogger = uiEventLogger;
|
mUiEventLogger = uiEventLogger;
|
||||||
|
mDreamCallbackController = dreamCallbackController;
|
||||||
|
|
||||||
final ViewModelStore viewModelStore = new ViewModelStore();
|
final ViewModelStore viewModelStore = new ViewModelStore();
|
||||||
final Complication.Host host =
|
final Complication.Host host =
|
||||||
@@ -217,6 +219,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
|
|||||||
public void onWakeUp(@NonNull Runnable onCompletedCallback) {
|
public void onWakeUp(@NonNull Runnable onCompletedCallback) {
|
||||||
mExecutor.execute(() -> {
|
mExecutor.execute(() -> {
|
||||||
if (mDreamOverlayContainerViewController != null) {
|
if (mDreamOverlayContainerViewController != null) {
|
||||||
|
mDreamCallbackController.onWakeUp();
|
||||||
mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
|
mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,22 +55,6 @@ public abstract class DreamOverlayModule {
|
|||||||
"dream_in_complications_translation_y";
|
"dream_in_complications_translation_y";
|
||||||
public static final String DREAM_IN_TRANSLATION_Y_DURATION =
|
public static final String DREAM_IN_TRANSLATION_Y_DURATION =
|
||||||
"dream_in_complications_translation_y_duration";
|
"dream_in_complications_translation_y_duration";
|
||||||
public static final String DREAM_OUT_TRANSLATION_Y_DISTANCE =
|
|
||||||
"dream_out_complications_translation_y";
|
|
||||||
public static final String DREAM_OUT_TRANSLATION_Y_DURATION =
|
|
||||||
"dream_out_complications_translation_y_duration";
|
|
||||||
public static final String DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM =
|
|
||||||
"dream_out_complications_translation_y_delay_bottom";
|
|
||||||
public static final String DREAM_OUT_TRANSLATION_Y_DELAY_TOP =
|
|
||||||
"dream_out_complications_translation_y_delay_top";
|
|
||||||
public static final String DREAM_OUT_ALPHA_DURATION =
|
|
||||||
"dream_out_complications_alpha_duration";
|
|
||||||
public static final String DREAM_OUT_ALPHA_DELAY_BOTTOM =
|
|
||||||
"dream_out_complications_alpha_delay_bottom";
|
|
||||||
public static final String DREAM_OUT_ALPHA_DELAY_TOP =
|
|
||||||
"dream_out_complications_alpha_delay_top";
|
|
||||||
public static final String DREAM_OUT_BLUR_DURATION =
|
|
||||||
"dream_out_blur_duration";
|
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
@Provides
|
@Provides
|
||||||
@@ -185,66 +169,6 @@ public abstract class DreamOverlayModule {
|
|||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayInTranslationYDurationMs);
|
return (long) resources.getInteger(R.integer.config_dreamOverlayInTranslationYDurationMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the number of pixels to translate complications when waking up from dream.
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_TRANSLATION_Y_DISTANCE)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static int providesDreamOutComplicationsTranslationY(@Main Resources resources) {
|
|
||||||
return resources.getDimensionPixelSize(R.dimen.dream_overlay_exit_y_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_TRANSLATION_Y_DURATION)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutComplicationsTranslationYDuration(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayOutTranslationYDurationMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutComplicationsTranslationYDelayBottom(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(
|
|
||||||
R.integer.config_dreamOverlayOutTranslationYDelayBottomMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_TRANSLATION_Y_DELAY_TOP)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutComplicationsTranslationYDelayTop(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayOutTranslationYDelayTopMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_ALPHA_DURATION)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutComplicationsAlphaDuration(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDurationMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_ALPHA_DELAY_BOTTOM)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutComplicationsAlphaDelayBottom(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDelayBottomMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_ALPHA_DELAY_TOP)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutComplicationsAlphaDelayTop(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDelayTopMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named(DREAM_OUT_BLUR_DURATION)
|
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
|
||||||
static long providesDreamOutBlurDuration(@Main Resources resources) {
|
|
||||||
return (long) resources.getInteger(R.integer.config_dreamOverlayOutBlurDurationMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@DreamOverlayComponent.DreamOverlayScope
|
@DreamOverlayComponent.DreamOverlayScope
|
||||||
static LifecycleOwner providesLifecycleOwner(Lazy<LifecycleRegistry> lifecycleRegistryLazy) {
|
static LifecycleOwner providesLifecycleOwner(Lazy<LifecycleRegistry> lifecycleRegistryLazy) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
|
|||||||
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
|
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
|
||||||
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
|
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
|
||||||
import static com.android.systemui.DejankUtils.whitelistIpcs;
|
import static com.android.systemui.DejankUtils.whitelistIpcs;
|
||||||
|
import static com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.TO_LOCKSCREEN_DURATION_MS;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -1231,8 +1232,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
|||||||
|
|
||||||
mDreamOpenAnimationDuration = context.getResources().getInteger(
|
mDreamOpenAnimationDuration = context.getResources().getInteger(
|
||||||
com.android.internal.R.integer.config_dreamOpenAnimationDuration);
|
com.android.internal.R.integer.config_dreamOpenAnimationDuration);
|
||||||
mDreamCloseAnimationDuration = context.getResources().getInteger(
|
mDreamCloseAnimationDuration = (int) TO_LOCKSCREEN_DURATION_MS;
|
||||||
com.android.internal.R.integer.config_dreamCloseAnimationDuration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void userActivity() {
|
public void userActivity() {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import com.android.systemui.doze.DozeHost
|
|||||||
import com.android.systemui.doze.DozeMachine
|
import com.android.systemui.doze.DozeMachine
|
||||||
import com.android.systemui.doze.DozeTransitionCallback
|
import com.android.systemui.doze.DozeTransitionCallback
|
||||||
import com.android.systemui.doze.DozeTransitionListener
|
import com.android.systemui.doze.DozeTransitionListener
|
||||||
|
import com.android.systemui.dreams.DreamCallbackController
|
||||||
|
import com.android.systemui.dreams.DreamCallbackController.DreamCallback
|
||||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||||
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
|
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
|
||||||
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
|
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
|
||||||
@@ -47,6 +49,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
|
||||||
/** Defines interface for classes that encapsulate application state for the keyguard. */
|
/** Defines interface for classes that encapsulate application state for the keyguard. */
|
||||||
interface KeyguardRepository {
|
interface KeyguardRepository {
|
||||||
@@ -176,6 +179,7 @@ constructor(
|
|||||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||||
private val dozeTransitionListener: DozeTransitionListener,
|
private val dozeTransitionListener: DozeTransitionListener,
|
||||||
private val authController: AuthController,
|
private val authController: AuthController,
|
||||||
|
private val dreamCallbackController: DreamCallbackController,
|
||||||
) : KeyguardRepository {
|
) : KeyguardRepository {
|
||||||
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
|
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
|
||||||
override val animateBottomAreaDozingTransitions =
|
override val animateBottomAreaDozingTransitions =
|
||||||
@@ -276,22 +280,35 @@ constructor(
|
|||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
||||||
override val isDreaming: Flow<Boolean> =
|
override val isDreaming: Flow<Boolean> =
|
||||||
conflatedCallbackFlow {
|
merge(
|
||||||
val callback =
|
conflatedCallbackFlow {
|
||||||
object : KeyguardUpdateMonitorCallback() {
|
val callback =
|
||||||
override fun onDreamingStateChanged(isDreaming: Boolean) {
|
object : KeyguardUpdateMonitorCallback() {
|
||||||
trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
|
override fun onDreamingStateChanged(isDreaming: Boolean) {
|
||||||
|
trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
keyguardUpdateMonitor.registerCallback(callback)
|
||||||
keyguardUpdateMonitor.registerCallback(callback)
|
trySendWithFailureLogging(
|
||||||
trySendWithFailureLogging(
|
keyguardUpdateMonitor.isDreaming,
|
||||||
keyguardUpdateMonitor.isDreaming,
|
TAG,
|
||||||
TAG,
|
"initial isDreaming",
|
||||||
"initial isDreaming",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
|
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
|
||||||
}
|
},
|
||||||
|
conflatedCallbackFlow {
|
||||||
|
val callback =
|
||||||
|
object : DreamCallback {
|
||||||
|
override fun onWakeUp() {
|
||||||
|
trySendWithFailureLogging(false, TAG, "updated isDreaming")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dreamCallbackController.addCallback(callback)
|
||||||
|
|
||||||
|
awaitClose { dreamCallbackController.removeCallback(callback) }
|
||||||
|
}
|
||||||
|
)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
||||||
override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
|
override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ interface KeyguardTransitionRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begin a transition from one state to another. Will not start if another transition is in
|
* Begin a transition from one state to another. Transitions are interruptible, and will issue a
|
||||||
* progress.
|
* [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
|
||||||
*/
|
*/
|
||||||
fun startTransition(info: TransitionInfo): UUID?
|
fun startTransition(info: TransitionInfo): UUID?
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ 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.util.kotlin.sample
|
import com.android.systemui.util.kotlin.sample
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -110,7 +112,7 @@ constructor(
|
|||||||
name,
|
name,
|
||||||
KeyguardState.DREAMING,
|
KeyguardState.DREAMING,
|
||||||
KeyguardState.LOCKSCREEN,
|
KeyguardState.LOCKSCREEN,
|
||||||
getAnimator(),
|
getAnimator(TO_LOCKSCREEN_DURATION),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -167,14 +169,16 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAnimator(): ValueAnimator {
|
private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
|
||||||
return ValueAnimator().apply {
|
return ValueAnimator().apply {
|
||||||
setInterpolator(Interpolators.LINEAR)
|
setInterpolator(Interpolators.LINEAR)
|
||||||
setDuration(TRANSITION_DURATION_MS)
|
setDuration(duration.inWholeMilliseconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TRANSITION_DURATION_MS = 500L
|
private val DEFAULT_DURATION = 500.milliseconds
|
||||||
|
val TO_LOCKSCREEN_DURATION = 1183.milliseconds
|
||||||
|
@JvmField val TO_LOCKSCREEN_DURATION_MS = TO_LOCKSCREEN_DURATION.inWholeMilliseconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,15 @@ 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.DREAMING
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
|
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
|
||||||
import com.android.systemui.keyguard.shared.model.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 javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
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
|
||||||
@@ -43,6 +46,10 @@ constructor(
|
|||||||
/** LOCKSCREEN->AOD transition information. */
|
/** LOCKSCREEN->AOD transition information. */
|
||||||
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
|
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
|
||||||
|
|
||||||
|
/** DREAMING->LOCKSCREEN transition information. */
|
||||||
|
val dreamingToLockscreenTransition: Flow<TransitionStep> =
|
||||||
|
repository.transition(DREAMING, LOCKSCREEN)
|
||||||
|
|
||||||
/** (any)->AOD transition information */
|
/** (any)->AOD transition information */
|
||||||
val anyStateToAodTransition: Flow<TransitionStep> =
|
val anyStateToAodTransition: Flow<TransitionStep> =
|
||||||
repository.transitions.filter { step -> step.to == KeyguardState.AOD }
|
repository.transitions.filter { step -> step.to == KeyguardState.AOD }
|
||||||
@@ -72,4 +79,21 @@ 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()
|
||||||
|
return flow
|
||||||
|
.map { step -> (step.value - start) * chunks }
|
||||||
|
.filter { value -> value >= 0f && value <= 1f }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.viewmodel
|
||||||
|
|
||||||
|
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||||
|
import com.android.systemui.keyguard.shared.model.AnimationParams
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
|
||||||
|
* consume.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class DreamingToLockscreenTransitionViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/** Dream overlay y-translation on exit */
|
||||||
|
fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
|
||||||
|
return flowForAnimation(DREAM_OVERLAY_TRANSLATION_Y).map { value ->
|
||||||
|
EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Dream overlay views alpha - fade out */
|
||||||
|
val dreamOverlayAlpha: Flow<Float> = flowForAnimation(DREAM_OVERLAY_ALPHA).map { 1f - it }
|
||||||
|
|
||||||
|
/** Dream background alpha - fade out */
|
||||||
|
val dreamBackgroundAlpha: Flow<Float> = flowForAnimation(DREAM_BACKGROUND_ALPHA).map { 1f - it }
|
||||||
|
|
||||||
|
/** Lockscreen views y-translation */
|
||||||
|
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||||
|
return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { it * translatePx }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lockscreen views alpha */
|
||||||
|
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
|
||||||
|
|
||||||
|
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
|
||||||
|
return interactor.transitionStepAnimation(
|
||||||
|
interactor.dreamingToLockscreenTransition,
|
||||||
|
params,
|
||||||
|
totalDuration = TO_LOCKSCREEN_DURATION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/* Length of time before ending the dream activity, in order to start unoccluding */
|
||||||
|
val DREAM_ANIMATION_DURATION = 250.milliseconds
|
||||||
|
|
||||||
|
val DREAM_OVERLAY_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
|
||||||
|
val DREAM_OVERLAY_ALPHA = AnimationParams(duration = 250.milliseconds)
|
||||||
|
val DREAM_BACKGROUND_ALPHA = AnimationParams(duration = 250.milliseconds)
|
||||||
|
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
|
||||||
|
val LOCKSCREEN_ALPHA =
|
||||||
|
AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.dreams
|
||||||
|
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.reset
|
||||||
|
import org.mockito.Mockito.times
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class DreamCallbackControllerTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var callback: DreamCallbackController.DreamCallback
|
||||||
|
|
||||||
|
private lateinit var underTest: DreamCallbackController
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
underTest = DreamCallbackController()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnWakeUpInvokesCallback() {
|
||||||
|
underTest.addCallback(callback)
|
||||||
|
underTest.onWakeUp()
|
||||||
|
verify(callback).onWakeUp()
|
||||||
|
|
||||||
|
// Adding twice should not invoke twice
|
||||||
|
reset(callback)
|
||||||
|
underTest.addCallback(callback)
|
||||||
|
underTest.onWakeUp()
|
||||||
|
verify(callback, times(1)).onWakeUp()
|
||||||
|
|
||||||
|
// After remove, no call to callback
|
||||||
|
reset(callback)
|
||||||
|
underTest.removeCallback(callback)
|
||||||
|
underTest.onWakeUp()
|
||||||
|
verify(callback, never()).onWakeUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,25 @@
|
|||||||
package com.android.systemui.dreams
|
package com.android.systemui.dreams
|
||||||
|
|
||||||
import android.animation.Animator
|
|
||||||
import android.animation.AnimatorSet
|
import android.animation.AnimatorSet
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.view.View
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.dreams.complication.ComplicationHostViewController
|
import com.android.systemui.dreams.complication.ComplicationHostViewController
|
||||||
|
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
|
||||||
import com.android.systemui.statusbar.BlurUtils
|
import com.android.systemui.statusbar.BlurUtils
|
||||||
import com.android.systemui.util.mockito.argumentCaptor
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
|
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.anyLong
|
||||||
|
import org.mockito.Mockito.eq
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.times
|
import org.mockito.Mockito.times
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
@@ -28,14 +35,6 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
|
|||||||
private const val DREAM_IN_COMPLICATIONS_ANIMATION_DURATION = 3L
|
private const val DREAM_IN_COMPLICATIONS_ANIMATION_DURATION = 3L
|
||||||
private const val DREAM_IN_TRANSLATION_Y_DISTANCE = 6
|
private const val DREAM_IN_TRANSLATION_Y_DISTANCE = 6
|
||||||
private const val DREAM_IN_TRANSLATION_Y_DURATION = 7L
|
private const val DREAM_IN_TRANSLATION_Y_DURATION = 7L
|
||||||
private const val DREAM_OUT_TRANSLATION_Y_DISTANCE = 6
|
|
||||||
private const val DREAM_OUT_TRANSLATION_Y_DURATION = 7L
|
|
||||||
private const val DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM = 8L
|
|
||||||
private const val DREAM_OUT_TRANSLATION_Y_DELAY_TOP = 9L
|
|
||||||
private const val DREAM_OUT_ALPHA_DURATION = 10L
|
|
||||||
private const val DREAM_OUT_ALPHA_DELAY_BOTTOM = 11L
|
|
||||||
private const val DREAM_OUT_ALPHA_DELAY_TOP = 12L
|
|
||||||
private const val DREAM_OUT_BLUR_DURATION = 13L
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mock private lateinit var mockAnimator: AnimatorSet
|
@Mock private lateinit var mockAnimator: AnimatorSet
|
||||||
@@ -43,6 +42,8 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var hostViewController: ComplicationHostViewController
|
@Mock private lateinit var hostViewController: ComplicationHostViewController
|
||||||
@Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
|
@Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
|
||||||
@Mock private lateinit var stateController: DreamOverlayStateController
|
@Mock private lateinit var stateController: DreamOverlayStateController
|
||||||
|
@Mock private lateinit var configController: ConfigurationController
|
||||||
|
@Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel
|
||||||
private lateinit var controller: DreamOverlayAnimationsController
|
private lateinit var controller: DreamOverlayAnimationsController
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -55,71 +56,48 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
|
|||||||
statusBarViewController,
|
statusBarViewController,
|
||||||
stateController,
|
stateController,
|
||||||
DREAM_BLUR_RADIUS,
|
DREAM_BLUR_RADIUS,
|
||||||
|
transitionViewModel,
|
||||||
|
configController,
|
||||||
DREAM_IN_BLUR_ANIMATION_DURATION,
|
DREAM_IN_BLUR_ANIMATION_DURATION,
|
||||||
DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
|
DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
|
||||||
DREAM_IN_TRANSLATION_Y_DISTANCE,
|
DREAM_IN_TRANSLATION_Y_DISTANCE,
|
||||||
DREAM_IN_TRANSLATION_Y_DURATION,
|
DREAM_IN_TRANSLATION_Y_DURATION,
|
||||||
DREAM_OUT_TRANSLATION_Y_DISTANCE,
|
|
||||||
DREAM_OUT_TRANSLATION_Y_DURATION,
|
|
||||||
DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM,
|
|
||||||
DREAM_OUT_TRANSLATION_Y_DELAY_TOP,
|
|
||||||
DREAM_OUT_ALPHA_DURATION,
|
|
||||||
DREAM_OUT_ALPHA_DELAY_BOTTOM,
|
|
||||||
DREAM_OUT_ALPHA_DELAY_TOP,
|
|
||||||
DREAM_OUT_BLUR_DURATION
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val mockView: View = mock()
|
||||||
|
whenever(mockView.resources).thenReturn(mContext.resources)
|
||||||
|
|
||||||
|
runBlocking(Dispatchers.Main.immediate) { controller.init(mockView) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testExitAnimationOnEnd() {
|
fun testWakeUpCallsExecutor() {
|
||||||
val mockCallback: () -> Unit = mock()
|
val mockExecutor: DelayableExecutor = mock()
|
||||||
|
val mockCallback: Runnable = mock()
|
||||||
|
|
||||||
controller.startExitAnimations(
|
controller.wakeUp(
|
||||||
view = mock(),
|
|
||||||
doneCallback = mockCallback,
|
doneCallback = mockCallback,
|
||||||
animatorBuilder = { mockAnimator }
|
executor = mockExecutor,
|
||||||
)
|
)
|
||||||
|
|
||||||
val captor = argumentCaptor<Animator.AnimatorListener>()
|
verify(mockExecutor).executeDelayed(eq(mockCallback), anyLong())
|
||||||
verify(mockAnimator).addListener(captor.capture())
|
|
||||||
val listener = captor.value
|
|
||||||
|
|
||||||
verify(mockCallback, never()).invoke()
|
|
||||||
listener.onAnimationEnd(mockAnimator)
|
|
||||||
verify(mockCallback, times(1)).invoke()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCancellation() {
|
fun testWakeUpAfterStartWillCancel() {
|
||||||
controller.startExitAnimations(
|
|
||||||
view = mock(),
|
|
||||||
doneCallback = mock(),
|
|
||||||
animatorBuilder = { mockAnimator }
|
|
||||||
)
|
|
||||||
|
|
||||||
verify(mockAnimator, never()).cancel()
|
|
||||||
controller.cancelAnimations()
|
|
||||||
verify(mockAnimator, times(1)).cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testExitAfterStartWillCancel() {
|
|
||||||
val mockStartAnimator: AnimatorSet = mock()
|
val mockStartAnimator: AnimatorSet = mock()
|
||||||
val mockExitAnimator: AnimatorSet = mock()
|
|
||||||
|
|
||||||
controller.startEntryAnimations(view = mock(), animatorBuilder = { mockStartAnimator })
|
controller.startEntryAnimations(animatorBuilder = { mockStartAnimator })
|
||||||
|
|
||||||
verify(mockStartAnimator, never()).cancel()
|
verify(mockStartAnimator, never()).cancel()
|
||||||
|
|
||||||
controller.startExitAnimations(
|
controller.wakeUp(
|
||||||
view = mock(),
|
|
||||||
doneCallback = mock(),
|
doneCallback = mock(),
|
||||||
animatorBuilder = { mockExitAnimator }
|
executor = mock(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Verify that we cancelled the start animator in favor of the exit
|
// Verify that we cancelled the start animator in favor of the exit
|
||||||
// animator.
|
// animator.
|
||||||
verify(mockStartAnimator, times(1)).cancel()
|
verify(mockStartAnimator, times(1)).cancel()
|
||||||
verify(mockExitAnimator, never()).cancel()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
mController.onViewAttached();
|
mController.onViewAttached();
|
||||||
|
|
||||||
verify(mAnimationsController).startEntryAnimations(mDreamOverlayContainerView);
|
verify(mAnimationsController).startEntryAnimations();
|
||||||
verify(mAnimationsController, never()).cancelAnimations();
|
verify(mAnimationsController, never()).cancelAnimations();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
|||||||
|
|
||||||
mController.onViewAttached();
|
mController.onViewAttached();
|
||||||
|
|
||||||
verify(mAnimationsController, never()).startEntryAnimations(mDreamOverlayContainerView);
|
verify(mAnimationsController, never()).startEntryAnimations();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
|||||||
@Mock
|
@Mock
|
||||||
UiEventLogger mUiEventLogger;
|
UiEventLogger mUiEventLogger;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
DreamCallbackController mDreamCallbackController;
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
ArgumentCaptor<View> mViewCaptor;
|
ArgumentCaptor<View> mViewCaptor;
|
||||||
|
|
||||||
@@ -141,7 +144,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
|||||||
mStateController,
|
mStateController,
|
||||||
mKeyguardUpdateMonitor,
|
mKeyguardUpdateMonitor,
|
||||||
mUiEventLogger,
|
mUiEventLogger,
|
||||||
LOW_LIGHT_COMPONENT);
|
LOW_LIGHT_COMPONENT,
|
||||||
|
mDreamCallbackController);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -353,6 +357,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
|||||||
mService.onWakeUp(callback);
|
mService.onWakeUp(callback);
|
||||||
mMainExecutor.runAllReady();
|
mMainExecutor.runAllReady();
|
||||||
verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
|
verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
|
||||||
|
verify(mDreamCallbackController).onWakeUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import com.android.systemui.doze.DozeHost
|
|||||||
import com.android.systemui.doze.DozeMachine
|
import com.android.systemui.doze.DozeMachine
|
||||||
import com.android.systemui.doze.DozeTransitionCallback
|
import com.android.systemui.doze.DozeTransitionCallback
|
||||||
import com.android.systemui.doze.DozeTransitionListener
|
import com.android.systemui.doze.DozeTransitionListener
|
||||||
|
import com.android.systemui.dreams.DreamCallbackController
|
||||||
|
import com.android.systemui.dreams.DreamCallbackController.DreamCallback
|
||||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||||
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
|
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
|
||||||
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
|
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
|
||||||
@@ -66,6 +68,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
|
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
|
||||||
@Mock private lateinit var authController: AuthController
|
@Mock private lateinit var authController: AuthController
|
||||||
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
|
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
|
||||||
|
@Mock private lateinit var dreamCallbackController: DreamCallbackController
|
||||||
|
|
||||||
private lateinit var underTest: KeyguardRepositoryImpl
|
private lateinit var underTest: KeyguardRepositoryImpl
|
||||||
|
|
||||||
@@ -83,6 +86,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
|||||||
keyguardUpdateMonitor,
|
keyguardUpdateMonitor,
|
||||||
dozeTransitionListener,
|
dozeTransitionListener,
|
||||||
authController,
|
authController,
|
||||||
|
dreamCallbackController,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +322,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isDreaming() =
|
fun isDreamingFromKeyguardUpdateMonitor() =
|
||||||
runTest(UnconfinedTestDispatcher()) {
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
|
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
@@ -338,6 +342,26 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isDreamingFromDreamCallbackController() =
|
||||||
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
|
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(true)
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
val listener =
|
||||||
|
withArgCaptor<DreamCallbackController.DreamCallback> {
|
||||||
|
verify(dreamCallbackController).addCallback(capture())
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.onWakeUp()
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun biometricUnlockState() =
|
fun biometricUnlockState() =
|
||||||
runTest(UnconfinedTestDispatcher()) {
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* 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.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.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||||
|
import com.android.systemui.keyguard.shared.model.AnimationParams
|
||||||
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||||
|
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.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_OVERLAY_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 DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
||||||
|
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
|
||||||
|
private lateinit var repository: FakeKeyguardTransitionRepository
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
repository = FakeKeyguardTransitionRepository()
|
||||||
|
val interactor = KeyguardTransitionInteractor(repository)
|
||||||
|
underTest = DreamingToLockscreenTransitionViewModel(interactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dreamOverlayTranslationY() =
|
||||||
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
|
val values = mutableListOf<Float>()
|
||||||
|
|
||||||
|
val pixels = 100
|
||||||
|
val job =
|
||||||
|
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f))
|
||||||
|
repository.sendTransitionStep(step(0.3f))
|
||||||
|
repository.sendTransitionStep(step(0.5f))
|
||||||
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
|
// Only two values should be present, since the dream overlay runs for a small fraction
|
||||||
|
// of
|
||||||
|
// the overall animation time
|
||||||
|
assertThat(values.size).isEqualTo(2)
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dreamOverlayFadeOut() =
|
||||||
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
|
val values = mutableListOf<Float>()
|
||||||
|
|
||||||
|
val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f))
|
||||||
|
repository.sendTransitionStep(step(0.1f))
|
||||||
|
repository.sendTransitionStep(step(0.5f))
|
||||||
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
|
// Only two values should be present, since the dream overlay runs for a small fraction
|
||||||
|
// of
|
||||||
|
// the overall animation time
|
||||||
|
assertThat(values.size).isEqualTo(2)
|
||||||
|
assertThat(values[0]).isEqualTo(1f - animValue(0f, DREAM_OVERLAY_ALPHA))
|
||||||
|
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, DREAM_OVERLAY_ALPHA))
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun animValue(stepValue: Float, params: AnimationParams): Float {
|
||||||
|
val totalDuration = TO_LOCKSCREEN_DURATION
|
||||||
|
val startValue = (params.startTime / totalDuration).toFloat()
|
||||||
|
|
||||||
|
val multiplier = (totalDuration / params.duration).toFloat()
|
||||||
|
return (stepValue - startValue) * multiplier
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun step(value: Float): TransitionStep {
|
||||||
|
return TransitionStep(
|
||||||
|
from = KeyguardState.DREAMING,
|
||||||
|
to = KeyguardState.LOCKSCREEN,
|
||||||
|
value = value,
|
||||||
|
transitionState = TransitionState.RUNNING,
|
||||||
|
ownerName = "DreamingToLockscreenTransitionViewModelTest"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user