Fix multiple issues with DREAM => LOCKSCREEN transition

- The transition is now playing all parts in parallel (previously: serial) and according to spec -> proper curves, proper timings
- All parts of the transition are now based on the transition framework and are guaranteed to be synchronized
- The animations were not playing at all due to a bug in transition framework

Spec: https://direct.googleplex.com/#/spec/277250003

Bug: b/267520000
Test: atest SystemUITests
Test: manually tested and aligned with spec
Change-Id: I0a8e096d9e6c08f0ce2e7736fb5679951602cfe0
Merged-In: I0a8e096d9e6c08f0ce2e7736fb5679951602cfe0
This commit is contained in:
Andreas Miko
2023-05-19 16:53:01 +00:00
parent f4f5ce437c
commit d6c544b125
43 changed files with 329 additions and 253 deletions

View File

@@ -71,13 +71,7 @@ public abstract class DreamOverlayService extends Service {
@Override
public void wakeUp() {
mService.wakeUp(this, () -> {
try {
mDreamOverlayCallback.onWakeUpComplete();
} catch (RemoteException e) {
Log.e(TAG, "Could not notify dream of wakeUp", e);
}
});
mService.wakeUp(this);
}
@Override
@@ -125,14 +119,14 @@ public abstract class DreamOverlayService extends Service {
mCurrentClient = null;
}
private void wakeUp(OverlayClient client, Runnable callback) {
private void wakeUp(OverlayClient client) {
// Run on executor as this is a binder call from OverlayClient.
mExecutor.execute(() -> {
if (mCurrentClient != client) {
return;
}
onWakeUp(callback);
onWakeUp();
});
}
@@ -190,19 +184,10 @@ public abstract class DreamOverlayService extends Service {
/**
* This method is overridden by implementations to handle when the dream has been requested
* to wakeup. This allows any overlay animations to run. By default, the method will invoke
* the callback immediately.
*
* This callback will be run on the {@link Executor} provided in the constructor if provided, or
* on the main executor if none was provided.
*
* @param onCompleteCallback The callback to trigger to notify the dream service that the
* overlay has completed waking up.
* to wakeup.
* @hide
*/
public void onWakeUp(@NonNull Runnable onCompleteCallback) {
onCompleteCallback.run();
}
public void onWakeUp() {}
/**
* This method is overridden by implementations to handle when the dream has ended. There may

View File

@@ -249,13 +249,6 @@ public class DreamService extends Service implements Window.Callback {
// Simply finish dream when exit is requested.
mHandler.post(() -> finish());
}
@Override
public void onWakeUpComplete() {
// Finish the dream once overlay animations are complete. Execute on handler since
// this is coming in on the overlay binder.
mHandler.post(() -> finish());
}
};
@@ -923,6 +916,7 @@ public class DreamService extends Service implements Window.Callback {
overlay.wakeUp();
} catch (RemoteException e) {
Slog.e(TAG, "Error waking the overlay service", e);
} finally {
finish();
}
});

View File

@@ -28,7 +28,4 @@ interface IDreamOverlayCallback {
* Invoked to request the dream exit.
*/
void onExitRequested();
/** Invoked when the dream overlay wakeUp animation is complete. */
void onWakeUpComplete();
}

View File

@@ -34,13 +34,11 @@ import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP
import com.android.systemui.complication.ComplicationLayoutParams.Position
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.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.Named
import kotlinx.coroutines.flow.MutableStateFlow
@@ -129,6 +127,12 @@ constructor(
)
}
}
launch {
transitionViewModel.transitionEnded.collect { _ ->
mOverlayStateController.setExitAnimationsRunning(false)
}
}
}
configController.removeCallback(configCallback)
@@ -251,9 +255,9 @@ constructor(
}
/** Starts the dream content and dream overlay exit animations. */
fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
fun wakeUp() {
cancelAnimations()
executor.executeDelayed(doneCallback, DREAM_ANIMATION_DURATION.inWholeMilliseconds)
mOverlayStateController.setExitAnimationsRunning(true)
}
/** Cancels the dream content and dream overlay animations, if they're currently running. */

View File

@@ -31,8 +31,6 @@ import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.android.app.animation.Interpolators;
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.systemui.R;
@@ -46,7 +44,6 @@ import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInt
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Arrays;
@@ -302,20 +299,15 @@ public class DreamOverlayContainerViewController extends
/**
* Handle the dream waking up and run any necessary animations.
*
* @param onAnimationEnd Callback to trigger once animations are finished.
* @param callbackExecutor Executor to execute the callback on.
*/
public void wakeUp(@NonNull Runnable onAnimationEnd,
@NonNull DelayableExecutor callbackExecutor) {
public void wakeUp() {
// When swiping causes wakeup, do not run any animations as the dream should exit as soon
// as possible.
if (mWakingUpFromSwipe) {
onAnimationEnd.run();
return;
}
mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
mDreamOverlayAnimationsController.wakeUp();
}
@Override

View File

@@ -116,6 +116,17 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
};
private final DreamOverlayStateController.Callback mExitAnimationFinishedCallback =
new DreamOverlayStateController.Callback() {
@Override
public void onStateChanged() {
if (!mStateController.areExitAnimationsRunning()) {
mStateController.removeCallback(mExitAnimationFinishedCallback);
resetCurrentDreamOverlayLocked();
}
}
};
private final DreamOverlayStateController mStateController;
@VisibleForTesting
@@ -257,10 +268,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
@Override
public void onWakeUp(@NonNull Runnable onCompletedCallback) {
public void onWakeUp() {
if (mDreamOverlayContainerViewController != null) {
mDreamOverlayCallbackController.onWakeUp();
mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
mDreamOverlayContainerViewController.wakeUp();
}
}
@@ -329,6 +340,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
private void resetCurrentDreamOverlayLocked() {
if (mStateController.areExitAnimationsRunning()) {
mStateController.addCallback(mExitAnimationFinishedCallback);
return;
}
if (mStarted && mWindow != null) {
try {
mWindowManager.removeView(mWindow.getDecorView());

View File

@@ -18,6 +18,7 @@ package com.android.systemui.keyguard;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -34,8 +35,8 @@ 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_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.LOCKSCREEN_ANIMATION_DURATION_MS;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -87,9 +88,11 @@ import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl.Transaction;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -127,12 +130,14 @@ import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -162,6 +167,9 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import kotlinx.coroutines.CoroutineDispatcher;
/**
* Mediates requests related to the keyguard. This includes queries about the
@@ -434,11 +442,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
*/
private final int mDreamOpenAnimationDuration;
/**
* The duration in milliseconds of the dream close animation.
*/
private final int mDreamCloseAnimationDuration;
/**
* The animation used for hiding keyguard. This is used to fetch the animation timings if
* WindowManager is not providing us with them.
@@ -1125,49 +1128,57 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
return;
}
final RemoteAnimationTarget primary = apps[0];
mRemoteAnimationTarget = apps[0];
final boolean isDream = (apps[0].taskInfo != null
&& apps[0].taskInfo.topActivityType
== WindowConfiguration.ACTIVITY_TYPE_DREAM);
final SyncRtSurfaceTransactionApplier applier =
new SyncRtSurfaceTransactionApplier(
mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
final View localView = mKeyguardViewControllerLazy.get()
.getViewRootImpl().getView();
final SyncRtSurfaceTransactionApplier applier =
new SyncRtSurfaceTransactionApplier(localView);
mContext.getMainExecutor().execute(() -> {
if (mUnoccludeAnimator != null) {
mUnoccludeAnimator.cancel();
}
if (isDream) {
initAlphaForAnimationTargets(wallpapers);
getRemoteSurfaceAlphaApplier().accept(0.0f);
mDreamingToLockscreenTransitionViewModel.get()
.startTransition();
return;
}
mUnoccludeAnimator = ValueAnimator.ofFloat(1f, 0f);
mUnoccludeAnimator.setDuration(isDream ? mDreamCloseAnimationDuration
: UNOCCLUDE_ANIMATION_DURATION);
mUnoccludeAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION);
mUnoccludeAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE);
mUnoccludeAnimator.addUpdateListener(
animation -> {
final float animatedValue =
(float) animation.getAnimatedValue();
final float surfaceHeight = primary.screenSpaceBounds.height();
final float surfaceHeight =
mRemoteAnimationTarget.screenSpaceBounds.height();
// Fade for all types of activities.
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
paramsBuilder =
new SyncRtSurfaceTransactionApplier.SurfaceParams
.Builder(primary.leash)
.Builder(mRemoteAnimationTarget.leash)
.withAlpha(animatedValue);
// Set translate if the occluding activity isn't Dream.
if (!isDream) {
mUnoccludeMatrix.setTranslate(
0f,
(1f - animatedValue)
* surfaceHeight
* UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT);
paramsBuilder.withMatrix(mUnoccludeMatrix).withCornerRadius(
mWindowCornerRadius);
}
mUnoccludeMatrix.setTranslate(
0f,
(1f - animatedValue)
* surfaceHeight
* UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT);
paramsBuilder.withMatrix(mUnoccludeMatrix).withCornerRadius(
mWindowCornerRadius);
applier.scheduleApply(paramsBuilder.build());
});
mUnoccludeAnimator.addListener(new AnimatorListenerAdapter() {
@@ -1189,6 +1200,34 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
};
private static void initAlphaForAnimationTargets(
@android.annotation.NonNull RemoteAnimationTarget[] targets
) {
for (RemoteAnimationTarget target : targets) {
if (target.mode != MODE_OPENING) continue;
try (Transaction t = new Transaction()) {
t.setAlpha(target.leash, 1.f);
t.apply();
}
}
}
private Consumer<Float> getRemoteSurfaceAlphaApplier() {
return (Float alpha) -> {
if (mRemoteAnimationTarget == null) return;
final View localView = mKeyguardViewControllerLazy.get().getViewRootImpl().getView();
final SyncRtSurfaceTransactionApplier applier =
new SyncRtSurfaceTransactionApplier(localView);
SyncRtSurfaceTransactionApplier.SurfaceParams
params =
new SyncRtSurfaceTransactionApplier.SurfaceParams
.Builder(mRemoteAnimationTarget.leash)
.withAlpha(alpha).build();
applier.scheduleApply(params);
};
}
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
@@ -1218,6 +1257,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private FeatureFlags mFeatureFlags;
private final UiEventLogger mUiEventLogger;
private final SessionTracker mSessionTracker;
private final CoroutineDispatcher mMainDispatcher;
private final Lazy<DreamingToLockscreenTransitionViewModel>
mDreamingToLockscreenTransitionViewModel;
private RemoteAnimationTarget mRemoteAnimationTarget;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -1253,7 +1296,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
Lazy<ScrimController> scrimControllerLazy,
FeatureFlags featureFlags) {
FeatureFlags featureFlags,
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel) {
mContext = context;
mUserTracker = userTracker;
mFalsingCollector = falsingCollector;
@@ -1308,11 +1353,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS;
mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS;
mFeatureFlags = featureFlags;
mUiEventLogger = uiEventLogger;
mSessionTracker = sessionTracker;
mMainDispatcher = mainDispatcher;
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
}
public void userActivity() {
@@ -1431,6 +1478,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mUpdateMonitor.registerCallback(mUpdateCallback);
adjustStatusBarLocked();
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
ViewRootImpl viewRootImpl = mKeyguardViewControllerLazy.get().getViewRootImpl();
if (viewRootImpl != null) {
collectFlow(viewRootImpl.getView(),
mDreamingToLockscreenTransitionViewModel.get().getDreamOverlayAlpha(),
getRemoteSurfaceAlphaApplier(), mMainDispatcher);
}
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.

View File

@@ -37,6 +37,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
@@ -51,6 +52,7 @@ import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionMo
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
@@ -72,6 +74,8 @@ import dagger.Provides;
import java.util.concurrent.Executor;
import kotlinx.coroutines.CoroutineDispatcher;
/**
* Dagger Module providing keyguard.
*/
@@ -128,7 +132,9 @@ public class KeyguardModule {
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
Lazy<ScrimController> scrimControllerLazy,
FeatureFlags featureFlags) {
FeatureFlags featureFlags,
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel) {
return new KeyguardViewMediator(
context,
uiEventLogger,
@@ -162,7 +168,9 @@ public class KeyguardModule {
notificationShadeWindowController,
activityLaunchAnimator,
scrimControllerLazy,
featureFlags);
featureFlags,
mainDispatcher,
dreamingToLockscreenTransitionViewModel);
}
/** */

View File

@@ -160,35 +160,28 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
// An animator was provided, so use it to run the transition
animator.setFloatValues(startingValue, 1f)
animator.duration = ((1f - startingValue) * animator.duration).toLong()
val updateListener =
object : AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator) {
emitTransition(
TransitionStep(
info,
(animation.getAnimatedValue() as Float),
TransitionState.RUNNING
)
)
}
}
val updateListener = AnimatorUpdateListener { animation ->
emitTransition(
TransitionStep(
info,
(animation.animatedValue as Float),
TransitionState.RUNNING
)
)
}
val adapter =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
}
override fun onAnimationCancel(animation: Animator) {
endAnimation(animation, lastStep.value, TransitionState.CANCELED)
endAnimation(lastStep.value, TransitionState.CANCELED)
}
override fun onAnimationEnd(animation: Animator) {
endAnimation(animation, 1f, TransitionState.FINISHED)
endAnimation(1f, TransitionState.FINISHED)
}
private fun endAnimation(
animation: Animator,
value: Float,
state: TransitionState
) {
private fun endAnimation(value: Float, state: TransitionState) {
emitTransition(TransitionStep(info, value, state))
animator.removeListener(this)
animator.removeUpdateListener(updateListener)
@@ -201,7 +194,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
return@startTransition null
}
?: run {
emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
// No animator, so it's manual. Provide a mechanism to callback
updateTransitionId = UUID.randomUUID()

View File

@@ -52,7 +52,7 @@ constructor(
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (wakefulnessModel, lastStartedTransition) ->
if (
wakefulnessModel.isStartingToWake() &&
wakefulnessModel.isStartingToWakeOrAwake() &&
lastStartedTransition.to == KeyguardState.DOZING
) {
keyguardTransitionRepository.startTransition(

View File

@@ -23,7 +23,6 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
@@ -48,39 +47,23 @@ constructor(
) : TransitionInteractor(FromDreamingTransitionInteractor::class.simpleName!!) {
override fun start() {
listenForDreamingToLockscreen()
listenForDreamingToOccluded()
listenForDreamingToGone()
listenForDreamingToDozing()
}
private fun listenForDreamingToLockscreen() {
fun startToLockscreenTransition() {
scope.launch {
keyguardInteractor.isAbleToDream
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
::Pair
),
::toTriple
if (keyguardTransitionInteractor.startedKeyguardState.value == KeyguardState.DREAMING) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.DREAMING,
KeyguardState.LOCKSCREEN,
getAnimator(TO_LOCKSCREEN_DURATION),
)
)
.collect { (isDreaming, dozeTransitionModel, lastStartedTransition) ->
if (
!isDreaming &&
isDozeOff(dozeTransitionModel.to) &&
lastStartedTransition.to == KeyguardState.DREAMING
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.DREAMING,
KeyguardState.LOCKSCREEN,
getAnimator(TO_LOCKSCREEN_DURATION),
)
)
}
}
}
}
}
@@ -173,6 +156,6 @@ constructor(
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 1183.milliseconds
val TO_LOCKSCREEN_DURATION = 1167.milliseconds
}
}

View File

@@ -111,7 +111,7 @@ constructor(
isDreaming && isDozeOff(dozeTransitionModel.to)
}
.sample(wakefulnessModel) { isAbleToDream, wakefulnessModel ->
isAbleToDream && wakefulnessModel.isStartingToWake()
isAbleToDream && wakefulnessModel.isStartingToWakeOrAwake()
}
.flatMapLatest { isAbleToDream ->
flow {

View File

@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -29,10 +30,14 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.stateIn
/** Encapsulates business-logic related to the keyguard transitions. */
@SysUISingleton
@@ -40,6 +45,7 @@ class KeyguardTransitionInteractor
@Inject
constructor(
private val repository: KeyguardTransitionRepository,
@Application val scope: CoroutineScope,
) {
/** (any)->GONE transition information */
val anyStateToGoneTransition: Flow<TransitionStep> =
@@ -108,10 +114,17 @@ constructor(
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
/** The last completed [KeyguardState] transition */
val finishedKeyguardState: Flow<KeyguardState> =
finishedKeyguardTransitionStep.map { step -> step.to }
/** The destination state of the last started transition */
val startedKeyguardState: StateFlow<KeyguardState> =
startedKeyguardTransitionStep
.map { step -> step.to }
.stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF)
/** The last completed [KeyguardState] transition */
val finishedKeyguardState: StateFlow<KeyguardState> =
finishedKeyguardTransitionStep
.map { step -> step.to }
.stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN)
/**
* The amount of transition into or out of the given [KeyguardState].
*

View File

@@ -29,6 +29,8 @@ data class WakefulnessModel(
fun isStartingToSleepOrAsleep() = isStartingToSleep() || state == WakefulnessState.ASLEEP
fun isStartingToWakeOrAwake() = isStartingToWake() || state == WakefulnessState.AWAKE
fun isStartingToSleepFromPowerButton() =
isStartingToSleep() && lastWakeReason == WakeSleepReason.POWER_BUTTON

View File

@@ -16,15 +16,17 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
/**
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -34,22 +36,32 @@ import kotlinx.coroutines.flow.Flow
class DreamingToLockscreenTransitionViewModel
@Inject
constructor(
private val interactor: KeyguardTransitionInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
) {
fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_LOCKSCREEN_DURATION,
transitionFlow = interactor.dreamingToLockscreenTransition,
transitionFlow = keyguardTransitionInteractor.dreamingToLockscreenTransition,
)
val transitionEnded =
keyguardTransitionInteractor.dreamingToLockscreenTransition.filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
/** Dream overlay y-translation on exit */
fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
return transitionAnimation.createFlow(
duration = 600.milliseconds,
duration = TO_LOCKSCREEN_DURATION,
onStep = { it * translatePx },
interpolator = EMPHASIZED_ACCELERATE,
interpolator = EMPHASIZED,
)
}
/** Dream overlay views alpha - fade out */
val dreamOverlayAlpha: Flow<Float> =
transitionAnimation.createFlow(
@@ -65,7 +77,7 @@ constructor(
// Reset on cancel or finish
onFinish = { 0f },
onCancel = { 0f },
interpolator = EMPHASIZED_DECELERATE,
interpolator = EMPHASIZED,
)
}
@@ -76,12 +88,4 @@ constructor(
duration = 250.milliseconds,
onStep = { it },
)
companion object {
/* Length of time before ending the dream activity, in order to start unoccluding */
val DREAM_ANIMATION_DURATION = 250.milliseconds
@JvmField
val LOCKSCREEN_ANIMATION_DURATION_MS =
(TO_LOCKSCREEN_DURATION - DREAM_ANIMATION_DURATION).inWholeMilliseconds
}
}

View File

@@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.Flow
class LockscreenToDreamingTransitionViewModel
@Inject
constructor(
private val interactor: KeyguardTransitionInteractor,
interactor: KeyguardTransitionInteractor,
) {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(

View File

@@ -47,6 +47,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.yield
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -118,7 +119,7 @@ class ClockEventControllerTest : SysuiTestCase() {
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
),
KeyguardTransitionInteractor(repository = transitionRepository),
KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,

View File

@@ -159,7 +159,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mVibrator,
mAuthRippleController,
mResources,
new KeyguardTransitionInteractor(mTransitionRepository),
new KeyguardTransitionInteractor(mTransitionRepository,
TestScopeProvider.getTestScope().getBackgroundScope()),
new KeyguardInteractor(
new FakeKeyguardRepository(),
mCommandQueue,

View File

@@ -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.keyguard
import kotlinx.coroutines.test.TestScope
class TestScopeProvider {
companion object {
@JvmStatic fun getTestScope() = TestScope()
}
}

View File

@@ -56,6 +56,7 @@ class LogContextInteractorImplTest : SysuiTestCase() {
foldProvider,
KeyguardTransitionInteractor(
keyguardTransitionRepository,
testScope.backgroundScope
),
)
}

View File

@@ -11,7 +11,6 @@ import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -23,9 +22,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -92,16 +89,10 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
}
@Test
fun testWakeUpCallsExecutor() {
val mockExecutor: DelayableExecutor = mock()
val mockCallback: Runnable = mock()
fun testWakeUpSetsExitAnimationsRunning() {
controller.wakeUp()
controller.wakeUp(
doneCallback = mockCallback,
executor = mockExecutor,
)
verify(mockExecutor).executeDelayed(eq(mockCallback), anyLong())
verify(stateController).setExitAnimationsRunning(true)
}
@Test
@@ -112,10 +103,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
verify(mockStartAnimator, never()).cancel()
controller.wakeUp(
doneCallback = mock(),
executor = mock(),
)
controller.wakeUp()
// Verify that we cancelled the start animator in favor of the exit
// animator.

View File

@@ -23,7 +23,6 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -498,18 +497,14 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
true /*shouldShowComplication*/);
mMainExecutor.runAllReady();
final Runnable callback = mock(Runnable.class);
mService.onWakeUp(callback);
mMainExecutor.runAllReady();
verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
mService.onWakeUp();
verify(mDreamOverlayContainerViewController).wakeUp();
verify(mDreamOverlayCallbackController).onWakeUp();
}
@Test
public void testWakeUpBeforeStartDoesNothing() {
final Runnable callback = mock(Runnable.class);
mService.onWakeUp(callback);
mMainExecutor.runAllReady();
verify(mDreamOverlayContainerViewController, never()).wakeUp(callback, mMainExecutor);
mService.onWakeUp();
verify(mDreamOverlayContainerViewController, never()).wakeUp();
}
}

View File

@@ -71,6 +71,7 @@ import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
@@ -105,6 +106,9 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.flow.Flow;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -156,6 +160,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock CentralSurfaces mCentralSurfaces;
private @Mock UiEventLogger mUiEventLogger;
private @Mock SessionTracker mSessionTracker;
private @Mock CoroutineDispatcher mDispatcher;
private @Mock DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
private FakeFeatureFlags mFeatureFlags;
@@ -171,6 +177,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
final ViewRootImpl testViewRoot = mock(ViewRootImpl.class);
when(testViewRoot.getView()).thenReturn(mock(View.class));
when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot);
when(mDreamingToLockscreenTransitionViewModel.getDreamOverlayAlpha())
.thenReturn(mock(Flow.class));
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mViewMediator, mKeyguardBypassController,
@@ -186,6 +194,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
}
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() {
// GIVEN keyguard is not enabled and isn't showing
mViewMediator.onSystemReady();
@@ -629,7 +638,9 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
() -> mNotificationShadeWindowController,
() -> mActivityLaunchAnimator,
() -> mScrimController,
mFeatureFlags);
mFeatureFlags,
mDispatcher,
() -> mDreamingToLockscreenTransitionViewModel);
mViewMediator.start();
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);

View File

@@ -70,7 +70,10 @@ class ResourceTrimmerTest : SysuiTestCase() {
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
KeyguardTransitionInteractor(keyguardTransitionRepository),
KeyguardTransitionInteractor(
keyguardTransitionRepository,
testScope.backgroundScope
),
globalWindowManager,
testScope.backgroundScope,
testDispatcher,

View File

@@ -215,7 +215,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
)
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val keyguardTransitionInteractor =
KeyguardTransitionInteractor(keyguardTransitionRepository)
KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,

View File

@@ -82,7 +82,8 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
bouncerRepository = FakeKeyguardBouncerRepository()
faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
keyguardTransitionInteractor = KeyguardTransitionInteractor(keyguardTransitionRepository)
keyguardTransitionInteractor =
KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
underTest =
SystemUIKeyguardFaceAuthInteractor(

View File

@@ -292,7 +292,8 @@ class KeyguardLongPressInteractorTest : SysuiTestCase() {
scope = testScope.backgroundScope,
transitionInteractor =
KeyguardTransitionInteractor(
repository = keyguardTransitionRepository,
keyguardTransitionRepository,
testScope.backgroundScope
),
repository = keyguardRepository,
logger = logger,

View File

@@ -27,11 +27,14 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
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 com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -46,11 +49,12 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
private lateinit var underTest: KeyguardTransitionInteractor
private lateinit var repository: FakeKeyguardTransitionRepository
private val testScope = TestScope()
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
underTest = KeyguardTransitionInteractor(repository)
underTest = KeyguardTransitionInteractor(repository, testScope.backgroundScope)
}
@Test
@@ -108,17 +112,17 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
}
@Test
fun keyguardStateTests() = runTest {
fun finishedKeyguardStateTests() = testScope.runTest {
val finishedSteps by collectValues(underTest.finishedKeyguardState)
runCurrent()
val steps = mutableListOf<TransitionStep>()
steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
steps.forEach {
@@ -126,7 +130,29 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
runCurrent()
}
assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD))
}
@Test
fun startedKeyguardStateTests() = testScope.runTest {
val finishedSteps by collectValues(underTest.startedKeyguardState)
runCurrent()
val steps = mutableListOf<TransitionStep>()
steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
steps.forEach {
repository.sendTransitionStep(it)
runCurrent()
}
assertThat(finishedSteps).isEqualTo(listOf(OFF, PRIMARY_BOUNCER, AOD, GONE))
}
@Test

View File

@@ -110,7 +110,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardInteractor = createKeyguardInteractor(featureFlags),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromLockscreenTransitionInteractor.start()
@@ -119,7 +120,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromDreamingTransitionInteractor.start()
@@ -128,7 +130,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromAodTransitionInteractor.start()
@@ -137,7 +140,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromGoneTransitionInteractor.start()
@@ -146,7 +150,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromDozingTransitionInteractor.start()
@@ -155,7 +160,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromOccludedTransitionInteractor.start()
@@ -164,7 +170,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromAlternateBouncerTransitionInteractor.start()
@@ -173,47 +180,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
keyguardSecurityModel = keyguardSecurityModel,
)
fromPrimaryBouncerTransitionInteractor.start()
}
@Test
fun dreamingToLockscreen() =
testScope.runTest {
// GIVEN a device is dreaming
keyguardRepository.setDreamingWithOverlay(true)
keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent()
// GIVEN a prior transition has run to DREAMING
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
// WHEN doze is complete
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
// AND dreaming has stopped
keyguardRepository.setDreamingWithOverlay(false)
advanceUntilIdle()
// AND then occluded has stopped
keyguardRepository.setKeyguardOccluded(false)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun lockscreenToPrimaryBouncerViaBouncerShowingCall() =
testScope.runTest {

View File

@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -45,7 +46,7 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
private val keyguardTransitionInteractor =
KeyguardTransitionInteractor(fakeKeyguardTransitionRepository)
KeyguardTransitionInteractor(fakeKeyguardTransitionRepository, TestScope().backgroundScope)
private lateinit var underTest: LightRevealScrimInteractor

View File

@@ -25,11 +25,12 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
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.KeyguardTransitionAnimationFlow
import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -42,13 +43,12 @@ import org.junit.runner.RunWith
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
private lateinit var transitionAnimation: KeyguardTransitionAnimationFlow
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
underTest = DreamingToLockscreenTransitionViewModel(interactor)
val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
}
@Test
@@ -60,17 +60,15 @@ class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
val job =
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(0.3f))
repository.sendTransitionStep(step(0.5f))
repository.sendTransitionStep(step(0.6f))
// ...up to here
repository.sendTransitionStep(step(0.8f))
repository.sendTransitionStep(step(1f))
assertThat(values.size).isEqualTo(5)
assertThat(values.size).isEqualTo(7)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
job.cancel()

View File

@@ -29,6 +29,7 @@ import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@ class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = GoneToDreamingTransitionViewModel(interactor)
}

View File

@@ -219,6 +219,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
transitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
scope = testScope.backgroundScope
),
repository = repository,
logger = UiEventLoggerFake(),

View File

@@ -29,6 +29,7 @@ import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = LockscreenToDreamingTransitionViewModel(interactor)
}

View File

@@ -29,6 +29,7 @@ import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = LockscreenToOccludedTransitionViewModel(interactor)
}

View File

@@ -29,6 +29,7 @@ import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@ class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = OccludedToLockscreenTransitionViewModel(interactor)
}

View File

@@ -33,6 +33,7 @@ import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -54,7 +55,7 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest =
PrimaryBouncerToGoneTransitionViewModel(
interactor,

View File

@@ -63,6 +63,7 @@ import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -146,7 +147,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
debugLogger,
mediaFlags,
keyguardUpdateMonitor,
KeyguardTransitionInteractor(repository = transitionRepository),
KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
)
verify(configurationController).addCallback(capture(configListener))
verify(mediaDataManager).addListener(capture(listener))

View File

@@ -99,6 +99,7 @@ class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = keyguardTransitionRepository,
scope = testScope.backgroundScope
),
falsingManager = falsingManager,
shadeController = shadeController,

View File

@@ -103,8 +103,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock
private lateinit var unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
@Mock private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
@@ -184,6 +184,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
scope = testScope.backgroundScope
),
falsingManager = FalsingManagerFake(),
shadeController = shadeController,

View File

@@ -59,6 +59,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -72,7 +73,6 @@ import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.Optional
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidTestingRunner::class)
@@ -106,7 +106,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Mock
private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock
private lateinit var unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
@@ -195,6 +196,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
scope = testScope.backgroundScope
),
falsingManager = FalsingManagerFake(),
shadeController = shadeController,

View File

@@ -47,7 +47,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
testScope = TestScope(UnconfinedTestDispatcher())
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(keyguardTransitionRepository)
val interactor =
KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
}

View File

@@ -101,12 +101,6 @@ public class DreamOverlayServiceTest {
mMonitor.onEndDream();
super.onEndDream();
}
@Override
public void onWakeUp(@NonNull Runnable onCompleteCallback) {
mMonitor.onWakeUp();
super.onWakeUp(onCompleteCallback);
}
}
/**