diff options
| author | 2023-01-10 03:27:47 +0000 | |
|---|---|---|
| committer | 2023-01-10 03:27:47 +0000 | |
| commit | cdecfd692de64376731fb7207b94cc860c11fdd9 (patch) | |
| tree | 8725d1f1a22e5875ab7d14e6b4d94de55cbf7207 | |
| parent | c2b4636770230ec900e8f9659ef01db239093ca0 (diff) | |
| parent | d991f01a22c3b8cc49a29dabb03acf631d83601d (diff) | |
[DO NOT MERGE] Transitions: LOCKSCREEN->DREAMING am: d991f01a22
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20835498
Change-Id: I5e0de8a6e93bf5fe6b4a20a39fdd52834afe898b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
31 files changed, 1038 insertions, 249 deletions
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ecb656091478..6841bf897e92 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1279,6 +1279,12 @@ <!-- OCCLUDED -> LOCKSCREEN transition: Amount to shift lockscreen content on entering --> <dimen name="occluded_to_lockscreen_transition_lockscreen_translation_y">40dp</dimen> + <!-- LOCKSCREEN -> DREAMING transition: Amount to shift lockscreen content on entering --> + <dimen name="lockscreen_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen> + + <!-- LOCKSCREEN -> OCCLUDED transition: Amount to shift lockscreen content on entering --> + <dimen name="lockscreen_to_occluded_transition_lockscreen_translation_y">-40dp</dimen> + <!-- The amount of vertical offset for the keyguard during the full shade transition. --> <dimen name="lockscreen_shade_keyguard_transition_vertical_offset">0dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e644ed65da6c..fa8d27391871 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -34,6 +34,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR 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 android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -122,6 +123,8 @@ import com.android.systemui.classifier.FalsingCollector; 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.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -507,6 +510,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private CentralSurfaces mCentralSurfaces; + private boolean mUnocclusionTransitionFlagEnabled = false; + private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -958,8 +963,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { - setOccluded(true /* isOccluded */, true /* animate */); - + if (!mUnocclusionTransitionFlagEnabled) { + setOccluded(true /* isOccluded */, true /* animate */); + } if (apps == null || apps.length == 0 || apps[0] == null) { if (DEBUG) { Log.d(TAG, "No apps provided to the OccludeByDream runner; " @@ -1001,9 +1007,20 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, applier.scheduleApply(paramsBuilder.build()); }); mOccludeByDreamAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mIsCancelled = false; + @Override + public void onAnimationCancel(Animator animation) { + mIsCancelled = true; + } + @Override public void onAnimationEnd(Animator animation) { try { + if (!mIsCancelled && mUnocclusionTransitionFlagEnabled) { + // We're already on the main thread, don't queue this call + handleSetOccluded(true /* isOccluded */, + false /* animate */); + } finishedCallback.onAnimationFinished(); mOccludeByDreamAnimator = null; } catch (RemoteException e) { @@ -1176,6 +1193,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, + FeatureFlags featureFlags, Lazy<ShadeController> shadeControllerLazy, Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, @@ -1230,9 +1248,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, R.dimen.physical_power_button_center_screen_location_y); mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); - mDreamOpenAnimationDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_dreamOpenAnimationDuration); + mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS; mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS; + mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); } public void userActivity() { @@ -1792,7 +1810,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Trace.beginSection("KeyguardViewMediator#setOccluded"); if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded); - mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD); mHandler.removeMessages(SET_OCCLUDED); Message msg = mHandler.obtainMessage(SET_OCCLUDED, isOccluded ? 1 : 0, animate ? 1 : 0); mHandler.sendMessage(msg); @@ -1825,6 +1842,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private void handleSetOccluded(boolean isOccluded, boolean animate) { Trace.beginSection("KeyguardViewMediator#handleSetOccluded"); Log.d(TAG, "handleSetOccluded(" + isOccluded + ")"); + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD); + synchronized (KeyguardViewMediator.this) { if (mHiding && isOccluded) { // We're in the process of going away but WindowManager wants to show a diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 47ef0fac17ab..98d3570106ce 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -39,6 +39,7 @@ import com.android.systemui.dagger.SysUISingleton; 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.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -112,6 +113,7 @@ public class KeyguardModule { ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, + FeatureFlags featureFlags, Lazy<ShadeController> shadeController, Lazy<NotificationShadeWindowController> notificationShadeWindowController, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, @@ -142,6 +144,7 @@ public class KeyguardModule { screenOnCoordinator, interactionJankMonitor, dreamOverlayStateController, + featureFlags, shadeController, notificationShadeWindowController, activityLaunchAnimator, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 343c2dc172fc..d14b66a68f11 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -135,11 +135,14 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio Log.i(TAG, "Duplicate call to start the transition, rejecting: $info") return null } - if (lastStep.transitionState != TransitionState.FINISHED) { - Log.i(TAG, "Transition still active: $lastStep, canceling") - } + val startingValue = + if (lastStep.transitionState != TransitionState.FINISHED) { + Log.i(TAG, "Transition still active: $lastStep, canceling") + lastStep.value + } else { + 0f + } - val startingValue = 1f - lastStep.value lastAnimator?.cancel() lastAnimator = info.animator diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 326acc9eb762..20c6531d580b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -30,6 +30,8 @@ import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.sample import java.util.UUID import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine @@ -51,6 +53,7 @@ constructor( override fun start() { listenForLockscreenToGone() listenForLockscreenToOccluded() + listenForLockscreenToCamera() listenForLockscreenToAod() listenForLockscreenToBouncer() listenForLockscreenToDreaming() @@ -69,7 +72,7 @@ constructor( name, KeyguardState.LOCKSCREEN, KeyguardState.DREAMING, - getAnimator(), + getAnimator(TO_DREAMING_DURATION), ) ) } @@ -184,17 +187,42 @@ constructor( ), ::toTriple ) - .collect { triple -> - val (isOccluded, keyguardState, isDreaming) = triple - // Occlusion signals come from the framework, and should interrupt any - // existing transition - if (isOccluded && !isDreaming) { + .collect { (isOccluded, keyguardState, isDreaming) -> + if (isOccluded && !isDreaming && keyguardState == KeyguardState.LOCKSCREEN) { keyguardTransitionRepository.startTransition( TransitionInfo( name, keyguardState, KeyguardState.OCCLUDED, - getAnimator(), + getAnimator(TO_OCCLUDED_DURATION), + ) + ) + } + } + } + } + + /** This signal may come in before the occlusion signal, and can provide a custom transition */ + private fun listenForLockscreenToCamera() { + scope.launch { + keyguardInteractor.onCameraLaunchDetected + .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) + .collect { (_, lastStartedStep) -> + // DREAMING/AOD/OFF may trigger on the first power button push, so include this + // state in order to cancel and correct the transition + if ( + lastStartedStep.to == KeyguardState.LOCKSCREEN || + lastStartedStep.to == KeyguardState.DREAMING || + lastStartedStep.to == KeyguardState.DOZING || + lastStartedStep.to == KeyguardState.AOD || + lastStartedStep.to == KeyguardState.OFF + ) { + keyguardTransitionRepository.startTransition( + TransitionInfo( + name, + KeyguardState.LOCKSCREEN, + KeyguardState.OCCLUDED, + getAnimator(TO_OCCLUDED_DURATION), ) ) } @@ -223,14 +251,16 @@ constructor( } } - private fun getAnimator(): ValueAnimator { + private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) - setDuration(TRANSITION_DURATION_MS) + setDuration(duration.inWholeMilliseconds) } } companion object { - private const val TRANSITION_DURATION_MS = 500L + private val DEFAULT_DURATION = 500.milliseconds + val TO_DREAMING_DURATION = 933.milliseconds + val TO_OCCLUDED_DURATION = 450.milliseconds } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 402c1793f0b2..ac2d230ee605 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -17,17 +17,24 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.StatusBarManager import android.graphics.Point +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel 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.DozeTransitionModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.CommandQueue.Callbacks import com.android.systemui.util.kotlin.sample import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -41,6 +48,7 @@ class KeyguardInteractor @Inject constructor( private val repository: KeyguardRepository, + private val commandQueue: CommandQueue, ) { /** * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at @@ -58,6 +66,23 @@ constructor( val isDreaming: Flow<Boolean> = repository.isDreaming /** Whether the system is dreaming with an overlay active */ val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay + /** Event for when the camera gesture is detected */ + val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow { + val callback = + object : CommandQueue.Callbacks { + override fun onCameraLaunchGestureDetected(source: Int) { + trySendWithFailureLogging( + cameraLaunchSourceIntToModel(source), + TAG, + "updated onCameraLaunchGestureDetected" + ) + } + } + + commandQueue.addCallback(callback) + + awaitClose { commandQueue.removeCallback(callback) } + } /** * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means @@ -103,4 +128,21 @@ constructor( fun isKeyguardShowing(): Boolean { return repository.isKeyguardShowing() } + + private fun cameraLaunchSourceIntToModel(value: Int): CameraLaunchSourceModel { + return when (value) { + StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE -> CameraLaunchSourceModel.WIGGLE + StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP -> + CameraLaunchSourceModel.POWER_DOUBLE_TAP + StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER -> + CameraLaunchSourceModel.LIFT_TRIGGER + StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE -> + CameraLaunchSourceModel.QUICK_AFFORDANCE + else -> throw IllegalArgumentException("Invalid CameraLaunchSourceModel value: $value") + } + } + + companion object { + private const val TAG = "KeyguardInteractor" + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 04024be571c8..9cdbcda1343d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -42,24 +42,32 @@ class KeyguardTransitionInteractor constructor( repository: KeyguardTransitionRepository, ) { + /** (any)->AOD transition information */ + val anyStateToAodTransition: Flow<TransitionStep> = + repository.transitions.filter { step -> step.to == KeyguardState.AOD } + /** AOD->LOCKSCREEN transition information. */ val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN) - /** LOCKSCREEN->AOD transition information. */ - val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD) - /** DREAMING->LOCKSCREEN transition information. */ val dreamingToLockscreenTransition: Flow<TransitionStep> = repository.transition(DREAMING, LOCKSCREEN) + /** LOCKSCREEN->AOD transition information. */ + val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD) + + /** LOCKSCREEN->DREAMING transition information. */ + val lockscreenToDreamingTransition: Flow<TransitionStep> = + repository.transition(LOCKSCREEN, DREAMING) + + /** LOCKSCREEN->OCCLUDED transition information. */ + val lockscreenToOccludedTransition: Flow<TransitionStep> = + repository.transition(LOCKSCREEN, OCCLUDED) + /** OCCLUDED->LOCKSCREEN transition information. */ val occludedToLockscreenTransition: Flow<TransitionStep> = repository.transition(OCCLUDED, LOCKSCREEN) - /** (any)->AOD transition information */ - val anyStateToAodTransition: Flow<TransitionStep> = - repository.transitions.filter { step -> step.to == KeyguardState.AOD } - /** * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> * Lockscreen (0f). diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt new file mode 100644 index 000000000000..19baf7705546 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.keyguard.shared.model + +/** Camera launch sources */ +enum class CameraLaunchSourceModel { + /** Device is wiggled */ + WIGGLE, + /** Power button has been double tapped */ + POWER_DOUBLE_TAP, + /** Device has been lifted */ + LIFT_TRIGGER, + /** Quick affordance button has been pressed */ + QUICK_AFFORDANCE, +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt new file mode 100644 index 000000000000..d48f87deaaf4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.AnimationParams +import com.android.systemui.keyguard.shared.model.TransitionState +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Breaks down LOCKSCREEN->DREAMING transition into discrete steps for corresponding views to + * consume. + */ +@SysUISingleton +class LockscreenToDreamingTransitionViewModel +@Inject +constructor( + private val interactor: KeyguardTransitionInteractor, +) { + + /** Lockscreen views y-translation */ + fun lockscreenTranslationY(translatePx: Int): Flow<Float> { + return merge( + flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value -> + (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx) + }, + // On end, reset the translation to 0 + interactor.lockscreenToDreamingTransition + .filter { step -> step.transitionState == TransitionState.FINISHED } + .map { 0f } + ) + } + + /** Lockscreen views alpha */ + val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it } + + private fun flowForAnimation(params: AnimationParams): Flow<Float> { + return interactor.transitionStepAnimation( + interactor.lockscreenToDreamingTransition, + params, + totalDuration = TO_DREAMING_DURATION + ) + } + + companion object { + @JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds + + val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds) + val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt new file mode 100644 index 000000000000..22d292e92856 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.AnimationParams +import com.android.systemui.keyguard.shared.model.TransitionState +import javax.inject.Inject +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Breaks down LOCKSCREEN->OCCLUDED transition into discrete steps for corresponding views to + * consume. + */ +@SysUISingleton +class LockscreenToOccludedTransitionViewModel +@Inject +constructor( + private val interactor: KeyguardTransitionInteractor, +) { + + /** Lockscreen views y-translation */ + fun lockscreenTranslationY(translatePx: Int): Flow<Float> { + return merge( + flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value -> + (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx) + }, + // On end, reset the translation to 0 + interactor.lockscreenToOccludedTransition + .filter { step -> step.transitionState == TransitionState.FINISHED } + .map { 0f } + ) + } + + /** Lockscreen views alpha */ + val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it } + + private fun flowForAnimation(params: AnimationParams): Flow<Float> { + return interactor.transitionStepAnimation( + interactor.lockscreenToOccludedTransition, + params, + totalDuration = TO_OCCLUDED_DURATION + ) + } + + companion object { + val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_OCCLUDED_DURATION) + val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 8f512d0205b8..ecaabce70f8a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -144,6 +144,8 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.KeyguardMediaController; @@ -687,12 +689,16 @@ public final class NotificationPanelViewController implements Dumpable { private boolean mExpandLatencyTracking; private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; + private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; + private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; private KeyguardTransitionInteractor mKeyguardTransitionInteractor; private CoroutineDispatcher mMainDispatcher; - private boolean mIsToLockscreenTransitionRunning = false; + private boolean mIsOcclusionTransitionRunning = false; private int mDreamingToLockscreenTransitionTranslationY; private int mOccludedToLockscreenTransitionTranslationY; + private int mLockscreenToDreamingTransitionTranslationY; + private int mLockscreenToOccludedTransitionTranslationY; private boolean mUnocclusionTransitionFlagEnabled = false; private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */, @@ -711,13 +717,25 @@ public final class NotificationPanelViewController implements Dumpable { private final Consumer<TransitionStep> mDreamingToLockscreenTransition = (TransitionStep step) -> { - mIsToLockscreenTransitionRunning = + mIsOcclusionTransitionRunning = step.getTransitionState() == TransitionState.RUNNING; }; private final Consumer<TransitionStep> mOccludedToLockscreenTransition = (TransitionStep step) -> { - mIsToLockscreenTransitionRunning = + mIsOcclusionTransitionRunning = + step.getTransitionState() == TransitionState.RUNNING; + }; + + private final Consumer<TransitionStep> mLockscreenToDreamingTransition = + (TransitionStep step) -> { + mIsOcclusionTransitionRunning = + step.getTransitionState() == TransitionState.RUNNING; + }; + + private final Consumer<TransitionStep> mLockscreenToOccludedTransition = + (TransitionStep step) -> { + mIsOcclusionTransitionRunning = step.getTransitionState() == TransitionState.RUNNING; }; @@ -791,6 +809,8 @@ public final class NotificationPanelViewController implements Dumpable { KeyguardBottomAreaInteractor keyguardBottomAreaInteractor, DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel, OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel, + LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel, + LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel, @Main CoroutineDispatcher mainDispatcher, KeyguardTransitionInteractor keyguardTransitionInteractor, DumpManager dumpManager) { @@ -810,6 +830,8 @@ public final class NotificationPanelViewController implements Dumpable { mGutsManager = gutsManager; mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel; mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel; + mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel; + mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override @@ -1117,22 +1139,44 @@ public final class NotificationPanelViewController implements Dumpable { collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(), mDreamingToLockscreenTransition, mMainDispatcher); collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(), - toLockscreenTransitionAlpha(mNotificationStackScrollLayoutController), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY( mDreamingToLockscreenTransitionTranslationY), - toLockscreenTransitionY(mNotificationStackScrollLayoutController), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); // Occluded->Lockscreen collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), mOccludedToLockscreenTransition, mMainDispatcher); collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), - toLockscreenTransitionAlpha(mNotificationStackScrollLayoutController), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY( mOccludedToLockscreenTransitionTranslationY), - toLockscreenTransitionY(mNotificationStackScrollLayoutController), + setTransitionY(mNotificationStackScrollLayoutController), + mMainDispatcher); + + // Lockscreen->Dreaming + collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(), + mLockscreenToDreamingTransition, mMainDispatcher); + collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), + mMainDispatcher); + collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY( + mLockscreenToDreamingTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), + mMainDispatcher); + + // Lockscreen->Occluded + collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), + mLockscreenToOccludedTransition, mMainDispatcher); + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), + mMainDispatcher); + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY( + mLockscreenToOccludedTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); } } @@ -1173,6 +1217,10 @@ public final class NotificationPanelViewController implements Dumpable { R.dimen.dreaming_to_lockscreen_transition_lockscreen_translation_y); mOccludedToLockscreenTransitionTranslationY = mResources.getDimensionPixelSize( R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y); + mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize( + R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y); + mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize( + R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y); } private void updateViewControllers(KeyguardStatusView keyguardStatusView, @@ -1836,7 +1884,7 @@ public final class NotificationPanelViewController implements Dumpable { } private void updateClock() { - if (mIsToLockscreenTransitionRunning) { + if (mIsOcclusionTransitionRunning) { return; } float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha; @@ -2727,7 +2775,7 @@ public final class NotificationPanelViewController implements Dumpable { } else if (statusBarState == KEYGUARD || statusBarState == StatusBarState.SHADE_LOCKED) { mKeyguardBottomArea.setVisibility(View.VISIBLE); - if (!mIsToLockscreenTransitionRunning) { + if (!mIsOcclusionTransitionRunning) { mKeyguardBottomArea.setAlpha(1f); } } else { @@ -3596,7 +3644,7 @@ public final class NotificationPanelViewController implements Dumpable { } private void updateNotificationTranslucency() { - if (mIsToLockscreenTransitionRunning) { + if (mIsOcclusionTransitionRunning) { return; } float alpha = 1f; @@ -3654,7 +3702,7 @@ public final class NotificationPanelViewController implements Dumpable { } private void updateKeyguardBottomAreaAlpha() { - if (mIsToLockscreenTransitionRunning) { + if (mIsOcclusionTransitionRunning) { return; } // There are two possible panel expansion behaviors: @@ -5886,7 +5934,7 @@ public final class NotificationPanelViewController implements Dumpable { mCurrentPanelState = state; } - private Consumer<Float> toLockscreenTransitionAlpha( + private Consumer<Float> setTransitionAlpha( NotificationStackScrollLayoutController stackScroller) { return (Float alpha) -> { mKeyguardStatusViewController.setAlpha(alpha); @@ -5904,7 +5952,7 @@ public final class NotificationPanelViewController implements Dumpable { }; } - private Consumer<Float> toLockscreenTransitionY( + private Consumer<Float> setTransitionY( NotificationStackScrollLayoutController stackScroller) { return (Float translationY) -> { mKeyguardStatusViewController.setTranslationY(translationY, /* excludeMedia= */false); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 5c1ddd601f96..77307f3de19d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -16,6 +16,8 @@ package com.android.systemui.shade; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; + import android.app.StatusBarManager; import android.media.AudioManager; import android.media.session.MediaSessionLegacyHelper; @@ -39,6 +41,9 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.TransitionState; +import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; @@ -57,6 +62,7 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import java.io.PrintWriter; +import java.util.function.Consumer; import javax.inject.Inject; @@ -96,6 +102,13 @@ public class NotificationShadeWindowViewController { private final ShadeExpansionStateManager mShadeExpansionStateManager; private boolean mIsTrackingBarGesture = false; + private boolean mIsOcclusionTransitionRunning = false; + + private final Consumer<TransitionStep> mLockscreenToDreamingTransition = + (TransitionStep step) -> { + mIsOcclusionTransitionRunning = + step.getTransitionState() == TransitionState.RUNNING; + }; @Inject public NotificationShadeWindowViewController( @@ -119,6 +132,7 @@ public class NotificationShadeWindowViewController { PulsingGestureListener pulsingGestureListener, FeatureFlags featureFlags, KeyguardBouncerViewModel keyguardBouncerViewModel, + KeyguardTransitionInteractor keyguardTransitionInteractor, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory ) { mLockscreenShadeTransitionController = transitionController; @@ -148,6 +162,11 @@ public class NotificationShadeWindowViewController { keyguardBouncerViewModel, keyguardBouncerComponentFactory); } + + if (featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)) { + collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), + mLockscreenToDreamingTransition); + } } /** @@ -215,6 +234,10 @@ public class NotificationShadeWindowViewController { return true; } + if (mIsOcclusionTransitionRunning) { + return false; + } + mFalsingCollector.onTouchEvent(ev); mPulsingWakeupGestureHandler.onTouchEvent(ev); mStatusBarKeyguardViewManager.onTouch(ev); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index e8f8e25364b3..b4baa44985bb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -15,6 +15,7 @@ */ package com.android.keyguard +import com.android.systemui.statusbar.CommandQueue import android.content.BroadcastReceiver import android.testing.AndroidTestingRunner import android.view.View @@ -81,6 +82,7 @@ class ClockEventControllerTest : SysuiTestCase() { @Mock private lateinit var largeClockEvents: ClockFaceEvents @Mock private lateinit var parentView: View @Mock private lateinit var transitionRepository: KeyguardTransitionRepository + @Mock private lateinit var commandQueue: CommandQueue private lateinit var repository: FakeKeyguardRepository @Mock private lateinit var logBuffer: LogBuffer private lateinit var underTest: ClockEventController @@ -99,7 +101,7 @@ class ClockEventControllerTest : SysuiTestCase() { repository = FakeKeyguardRepository() underTest = ClockEventController( - KeyguardInteractor(repository = repository), + KeyguardInteractor(repository = repository, commandQueue = commandQueue), KeyguardTransitionInteractor(repository = transitionRepository), broadcastDispatcher, batteryController, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index e4c41a7ed804..05bd1e482950 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -49,6 +49,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -91,6 +92,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected @Mock AuthRippleController mAuthRippleController; protected @Mock FeatureFlags mFeatureFlags; protected @Mock KeyguardTransitionRepository mTransitionRepository; + protected @Mock CommandQueue mCommandQueue; protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); protected LockIconViewController mUnderTest; @@ -157,7 +159,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mAuthRippleController, mResources, new KeyguardTransitionInteractor(mTransitionRepository), - new KeyguardInteractor(new FakeKeyguardRepository()), + new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue), mFeatureFlags ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index 465976607a01..0a03b2c87f71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -51,6 +51,7 @@ import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any @@ -86,9 +87,9 @@ class CustomizationProviderTest : SysuiTestCase() { @Mock private lateinit var backgroundHandler: Handler @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage @Mock private lateinit var launchAnimator: DialogLaunchAnimator + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: CustomizationProvider - private lateinit var testScope: TestScope @Before @@ -160,6 +161,7 @@ class CustomizationProviderTest : SysuiTestCase() { keyguardInteractor = KeyguardInteractor( repository = FakeKeyguardRepository(), + commandQueue = commandQueue, ), registry = mock(), lockPatternUtils = lockPatternUtils, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 9b0d8dbe5a65..122d7fdbfa47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -66,6 +66,7 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -135,6 +136,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock AuthController mAuthController; private @Mock ShadeExpansionStateManager mShadeExpansionStateManager; private @Mock ShadeWindowLogger mShadeWindowLogger; + private @Mock FeatureFlags mFeatureFlags; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -510,6 +512,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOnCoordinator, mInteractionJankMonitor, mDreamOverlayStateController, + mFeatureFlags, () -> mShadeController, () -> mNotificationShadeWindowController, () -> mActivityLaunchAnimator, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index 5d2f0eb01de1..f8f2a56d4808 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -104,7 +104,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1)) assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN) - val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.9)) + val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.1)) assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD) job.cancel() @@ -201,7 +201,10 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { ) ) fractions.forEachIndexed { index, fraction -> - assertThat(steps[index + 1]) + val step = steps[index + 1] + val truncatedValue = + BigDecimal(step.value.toDouble()).setScale(2, RoundingMode.HALF_UP).toFloat() + assertThat(step.copy(value = truncatedValue)) .isEqualTo( TransitionStep( from, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt new file mode 100644 index 000000000000..68d13d354a43 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.app.StatusBarManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.CommandQueue.Callbacks +import com.android.systemui.util.mockito.argumentCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardInteractorTest : SysuiTestCase() { + @Mock private lateinit var commandQueue: CommandQueue + + private lateinit var underTest: KeyguardInteractor + private lateinit var repository: FakeKeyguardRepository + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + repository = FakeKeyguardRepository() + underTest = KeyguardInteractor(repository, commandQueue) + } + + @Test + fun onCameraLaunchDetected() = runTest { + val flow = underTest.onCameraLaunchDetected + var cameraLaunchSource = collectLastValue(flow) + runCurrent() + + val captor = argumentCaptor<CommandQueue.Callbacks>() + verify(commandQueue).addCallback(captor.capture()) + + captor.value.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) + assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE) + + captor.value.onCameraLaunchGestureDetected( + StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP + ) + assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP) + + captor.value.onCameraLaunchGestureDetected( + StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER + ) + assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER) + + captor.value.onCameraLaunchGestureDetected( + StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE + ) + assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE) + + flow.onCompletion { verify(commandQueue).removeCallback(captor.value) } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 14b7c311d9af..43287b03b36a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -44,6 +44,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any @@ -216,6 +217,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller @Mock private lateinit var expandable: Expandable @Mock private lateinit var launchAnimator: DialogLaunchAnimator + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: KeyguardQuickAffordanceInteractor @@ -286,7 +288,11 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { ) underTest = KeyguardQuickAffordanceInteractor( - keyguardInteractor = KeyguardInteractor(repository = FakeKeyguardRepository()), + keyguardInteractor = + KeyguardInteractor( + repository = FakeKeyguardRepository(), + commandQueue = commandQueue + ), registry = FakeKeyguardQuickAffordanceRegistry( mapOf( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 972919a9d27a..b75a15da641a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -46,6 +46,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock @@ -75,6 +76,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: KeyguardQuickAffordanceInteractor @@ -152,7 +154,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( - keyguardInteractor = KeyguardInteractor(repository = repository), + keyguardInteractor = + KeyguardInteractor(repository = repository, commandQueue = commandQueue), registry = FakeKeyguardQuickAffordanceRegistry( mapOf( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index d2b7838274a9..754adfdc48b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.keyguard.util.KeyguardTransitionRunner import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.cancelChildren @@ -66,6 +67,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // Used to verify transition requests for test output @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository + @Mock private lateinit var commandQueue: CommandQueue private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor @@ -85,7 +87,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor( scope = testScope, - keyguardInteractor = KeyguardInteractor(keyguardRepository), + keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), shadeRepository = shadeRepository, keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), @@ -95,7 +97,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDreamingTransitionInteractor = FromDreamingTransitionInteractor( scope = testScope, - keyguardInteractor = KeyguardInteractor(keyguardRepository), + keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index a2c2f711b1d4..022afdd61fc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -47,6 +47,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any @@ -84,6 +85,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: KeyguardBottomAreaViewModel @@ -124,7 +126,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { ) repository = FakeKeyguardRepository() - val keyguardInteractor = KeyguardInteractor(repository = repository) + val keyguardInteractor = + KeyguardInteractor(repository = repository, commandQueue = commandQueue) whenever(userTracker.userHandle).thenReturn(mock()) whenever(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt new file mode 100644 index 000000000000..739059126b04 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.AnimationParams +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { + private lateinit var underTest: LockscreenToDreamingTransitionViewModel + private lateinit var repository: FakeKeyguardTransitionRepository + + @Before + fun setUp() { + repository = FakeKeyguardTransitionRepository() + val interactor = KeyguardTransitionInteractor(repository) + underTest = LockscreenToDreamingTransitionViewModel(interactor) + } + + @Test + fun lockscreenFadeOut() = + runTest(UnconfinedTestDispatcher()) { + val values = mutableListOf<Float>() + + val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this) + + // Should start running here... + repository.sendTransitionStep(step(0f)) + repository.sendTransitionStep(step(0.1f)) + repository.sendTransitionStep(step(0.2f)) + // ...up to here + repository.sendTransitionStep(step(0.3f)) + repository.sendTransitionStep(step(1f)) + + // Only three values should be present, since the dream overlay runs for a small + // fraction + // of the overall animation time + assertThat(values.size).isEqualTo(3) + assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA)) + assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA)) + assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA)) + + job.cancel() + } + + @Test + fun lockscreenTranslationY() = + runTest(UnconfinedTestDispatcher()) { + val values = mutableListOf<Float>() + + val pixels = 100 + val job = + underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this) + + // Should start running here... + repository.sendTransitionStep(step(0f)) + repository.sendTransitionStep(step(0.3f)) + repository.sendTransitionStep(step(0.5f)) + // ...up to here + repository.sendTransitionStep(step(1f)) + + assertThat(values.size).isEqualTo(3) + assertThat(values[0]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(0f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + assertThat(values[1]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(0.3f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + assertThat(values[2]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(0.5f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + job.cancel() + } + + private fun animValue(stepValue: Float, params: AnimationParams): Float { + val totalDuration = TO_DREAMING_DURATION + val startValue = (params.startTime / totalDuration).toFloat() + + val multiplier = (totalDuration / params.duration).toFloat() + return (stepValue - startValue) * multiplier + } + + private fun step(value: Float): TransitionStep { + return TransitionStep( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + value = value, + transitionState = TransitionState.RUNNING, + ownerName = "LockscreenToDreamingTransitionViewModelTest" + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt new file mode 100644 index 000000000000..759345f51c59 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_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.LockscreenToOccludedTransitionViewModel.Companion.LOCKSCREEN_ALPHA +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { + private lateinit var underTest: LockscreenToOccludedTransitionViewModel + private lateinit var repository: FakeKeyguardTransitionRepository + + @Before + fun setUp() { + repository = FakeKeyguardTransitionRepository() + val interactor = KeyguardTransitionInteractor(repository) + underTest = LockscreenToOccludedTransitionViewModel(interactor) + } + + @Test + fun lockscreenFadeOut() = + runTest(UnconfinedTestDispatcher()) { + val values = mutableListOf<Float>() + + val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this) + + // Should start running here... + repository.sendTransitionStep(step(0f)) + repository.sendTransitionStep(step(0.1f)) + repository.sendTransitionStep(step(0.4f)) + // ...up to here + repository.sendTransitionStep(step(0.7f)) + repository.sendTransitionStep(step(1f)) + + // Only 3 values should be present, since the dream overlay runs for a small fraction + // of the overall animation time + assertThat(values.size).isEqualTo(3) + assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA)) + assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA)) + assertThat(values[2]).isEqualTo(1f - animValue(0.4f, LOCKSCREEN_ALPHA)) + + job.cancel() + } + + @Test + fun lockscreenTranslationY() = + runTest(UnconfinedTestDispatcher()) { + val values = mutableListOf<Float>() + + val pixels = 100 + val job = + underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this) + + // Should start running here... + repository.sendTransitionStep(step(0f)) + repository.sendTransitionStep(step(0.3f)) + repository.sendTransitionStep(step(0.5f)) + repository.sendTransitionStep(step(1f)) + // ...up to here + + assertThat(values.size).isEqualTo(4) + assertThat(values[0]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(0f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + assertThat(values[1]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(0.3f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + assertThat(values[2]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(0.5f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + assertThat(values[3]) + .isEqualTo( + EMPHASIZED_ACCELERATE.getInterpolation( + animValue(1f, LOCKSCREEN_TRANSLATION_Y) + ) * pixels + ) + job.cancel() + } + + private fun animValue(stepValue: Float, params: AnimationParams): Float { + val totalDuration = TO_OCCLUDED_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.LOCKSCREEN, + to = KeyguardState.OCCLUDED, + value = value, + transitionState = TransitionState.RUNNING, + ownerName = "LockscreenToOccludedTransitionViewModelTest" + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 65b2ac0eb2d3..896db4b43eb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -107,6 +107,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInterac import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.KeyguardMediaController; @@ -293,6 +295,9 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; @Mock private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; @Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; + @Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; + @Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; + @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Mock private CoroutineDispatcher mMainDispatcher; @Mock private MotionEvent mDownMotionEvent; @@ -513,6 +518,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mKeyguardBottomAreaInteractor, mDreamingToLockscreenTransitionViewModel, mOccludedToLockscreenTransitionViewModel, + mLockscreenToDreamingTransitionViewModel, + mLockscreenToOccludedTransitionViewModel, mMainDispatcher, mKeyguardTransitionInteractor, mDumpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index c3207c2f58a5..d5e6463dd380 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -101,6 +102,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var keyguardBouncerContainer: ViewGroup @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardHostViewController: KeyguardHostViewController + @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> private lateinit var interactionEventHandler: InteractionEventHandler @@ -132,7 +134,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { pulsingGestureListener, featureFlags, keyguardBouncerViewModel, - keyguardBouncerComponentFactory + keyguardTransitionInteractor, + keyguardBouncerComponentFactory, ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 4bf00c4ccb51..f1d8188612a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -40,6 +40,7 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -93,6 +94,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Mock private NotificationInsetsController mNotificationInsetsController; + @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> mInteractionEventHandlerCaptor; @@ -132,6 +134,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mPulsingGestureListener, mFeatureFlags, mKeyguardBouncerViewModel, + mKeyguardTransitionInteractor, mKeyguardBouncerComponentFactory ); mController.setupExpandedStatusBar(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt index 89402de792dc..30c4f97e5503 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.shade.NotificationPanelViewController +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.unfold.util.FoldableDeviceStates @@ -73,6 +74,8 @@ class FoldAodAnimationControllerTest : SysuiTestCase() { @Mock lateinit var viewTreeObserver: ViewTreeObserver + @Mock private lateinit var commandQueue: CommandQueue + @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> private lateinit var deviceStates: FoldableDeviceStates @@ -102,7 +105,8 @@ class FoldAodAnimationControllerTest : SysuiTestCase() { } keyguardRepository = FakeKeyguardRepository() - val keyguardInteractor = KeyguardInteractor(repository = keyguardRepository) + val keyguardInteractor = + KeyguardInteractor(repository = keyguardRepository, commandQueue = commandQueue) // Needs to be run on the main thread runBlocking(IMMEDIATE) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index 5cd415d93475..9bb52be276f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -93,6 +94,7 @@ class UserInteractorTest : SysuiTestCase() { @Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: UserInteractor @@ -136,6 +138,7 @@ class UserInteractorTest : SysuiTestCase() { keyguardInteractor = KeyguardInteractor( repository = keyguardRepository, + commandQueue = commandQueue, ), manager = manager, applicationScope = testScope.backgroundScope, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt index 108fa6246e9c..9a4ca5654691 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -75,6 +76,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: StatusBarUserChipViewModel @@ -241,6 +243,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { keyguardInteractor = KeyguardInteractor( repository = keyguardRepository, + commandQueue = commandQueue, ), featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 784a26bb371b..3d4bbdb23686 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -76,6 +77,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver + @Mock private lateinit var commandQueue: CommandQueue private lateinit var underTest: UserSwitcherViewModel @@ -142,6 +144,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { keyguardInteractor = KeyguardInteractor( repository = keyguardRepository, + commandQueue = commandQueue, ), featureFlags = FakeFeatureFlags().apply { @@ -169,273 +172,295 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @Test - fun users() = testScope.runTest { - val userInfos = - listOf( - UserInfo( - /* id= */ 0, - /* name= */ "zero", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - UserInfo( - /* id= */ 1, - /* name= */ "one", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - UserInfo( - /* id= */ 2, - /* name= */ "two", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - ) - userRepository.setUserInfos(userInfos) - userRepository.setSelectedUserInfo(userInfos[0]) + fun users() = + testScope.runTest { + val userInfos = + listOf( + UserInfo( + /* id= */ 0, + /* name= */ "zero", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_PRIMARY or + UserInfo.FLAG_ADMIN or + UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + UserInfo( + /* id= */ 1, + /* name= */ "one", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + UserInfo( + /* id= */ 2, + /* name= */ "two", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + ) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) - val userViewModels = mutableListOf<List<UserViewModel>>() - val job = launch(testDispatcher) { underTest.users.toList(userViewModels) } + val userViewModels = mutableListOf<List<UserViewModel>>() + val job = launch(testDispatcher) { underTest.users.toList(userViewModels) } - assertThat(userViewModels.last()).hasSize(3) - assertUserViewModel( - viewModel = userViewModels.last()[0], - viewKey = 0, - name = Text.Loaded("zero"), - isSelectionMarkerVisible = true, - ) - assertUserViewModel( - viewModel = userViewModels.last()[1], - viewKey = 1, - name = Text.Loaded("one"), - isSelectionMarkerVisible = false, - ) - assertUserViewModel( - viewModel = userViewModels.last()[2], - viewKey = 2, - name = Text.Loaded("two"), - isSelectionMarkerVisible = false, - ) - job.cancel() - } + assertThat(userViewModels.last()).hasSize(3) + assertUserViewModel( + viewModel = userViewModels.last()[0], + viewKey = 0, + name = Text.Loaded("zero"), + isSelectionMarkerVisible = true, + ) + assertUserViewModel( + viewModel = userViewModels.last()[1], + viewKey = 1, + name = Text.Loaded("one"), + isSelectionMarkerVisible = false, + ) + assertUserViewModel( + viewModel = userViewModels.last()[2], + viewKey = 2, + name = Text.Loaded("two"), + isSelectionMarkerVisible = false, + ) + job.cancel() + } @Test - fun `maximumUserColumns - few users`() = testScope.runTest { - setUsers(count = 2) - val values = mutableListOf<Int>() - val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) } + fun `maximumUserColumns - few users`() = + testScope.runTest { + setUsers(count = 2) + val values = mutableListOf<Int>() + val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) } - assertThat(values.last()).isEqualTo(4) + assertThat(values.last()).isEqualTo(4) - job.cancel() - } + job.cancel() + } @Test - fun `maximumUserColumns - many users`() = testScope.runTest { - setUsers(count = 5) - val values = mutableListOf<Int>() - val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) } - - assertThat(values.last()).isEqualTo(3) - job.cancel() - } + fun `maximumUserColumns - many users`() = + testScope.runTest { + setUsers(count = 5) + val values = mutableListOf<Int>() + val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) } + + assertThat(values.last()).isEqualTo(3) + job.cancel() + } @Test - fun `isOpenMenuButtonVisible - has actions - true`() = testScope.runTest { - setUsers(2) + fun `isOpenMenuButtonVisible - has actions - true`() = + testScope.runTest { + setUsers(2) - val isVisible = mutableListOf<Boolean>() - val job = launch(testDispatcher) { underTest.isOpenMenuButtonVisible.toList(isVisible) } + val isVisible = mutableListOf<Boolean>() + val job = launch(testDispatcher) { underTest.isOpenMenuButtonVisible.toList(isVisible) } - assertThat(isVisible.last()).isTrue() - job.cancel() - } + assertThat(isVisible.last()).isTrue() + job.cancel() + } @Test - fun `isOpenMenuButtonVisible - no actions - false`() = testScope.runTest { - val userInfos = setUsers(2) - userRepository.setSelectedUserInfo(userInfos[1]) - keyguardRepository.setKeyguardShowing(true) - whenever(manager.canAddMoreUsers(any())).thenReturn(false) - - val isVisible = mutableListOf<Boolean>() - val job = launch(testDispatcher) { underTest.isOpenMenuButtonVisible.toList(isVisible) } - - assertThat(isVisible.last()).isFalse() - job.cancel() - } + fun `isOpenMenuButtonVisible - no actions - false`() = + testScope.runTest { + val userInfos = setUsers(2) + userRepository.setSelectedUserInfo(userInfos[1]) + keyguardRepository.setKeyguardShowing(true) + whenever(manager.canAddMoreUsers(any())).thenReturn(false) + + val isVisible = mutableListOf<Boolean>() + val job = launch(testDispatcher) { underTest.isOpenMenuButtonVisible.toList(isVisible) } + + assertThat(isVisible.last()).isFalse() + job.cancel() + } @Test - fun menu() = testScope.runTest { - val isMenuVisible = mutableListOf<Boolean>() - val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) } - assertThat(isMenuVisible.last()).isFalse() + fun menu() = + testScope.runTest { + val isMenuVisible = mutableListOf<Boolean>() + val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) } + assertThat(isMenuVisible.last()).isFalse() - underTest.onOpenMenuButtonClicked() - assertThat(isMenuVisible.last()).isTrue() + underTest.onOpenMenuButtonClicked() + assertThat(isMenuVisible.last()).isTrue() - underTest.onMenuClosed() - assertThat(isMenuVisible.last()).isFalse() + underTest.onMenuClosed() + assertThat(isMenuVisible.last()).isFalse() - job.cancel() - } + job.cancel() + } @Test - fun `menu actions`() = testScope.runTest { - setUsers(2) - val actions = mutableListOf<List<UserActionViewModel>>() - val job = launch(testDispatcher) { underTest.menu.toList(actions) } - - assertThat(actions.last().map { it.viewKey }) - .isEqualTo( - listOf( - UserActionModel.ENTER_GUEST_MODE.ordinal.toLong(), - UserActionModel.ADD_USER.ordinal.toLong(), - UserActionModel.ADD_SUPERVISED_USER.ordinal.toLong(), - UserActionModel.NAVIGATE_TO_USER_MANAGEMENT.ordinal.toLong(), + fun `menu actions`() = + testScope.runTest { + setUsers(2) + val actions = mutableListOf<List<UserActionViewModel>>() + val job = launch(testDispatcher) { underTest.menu.toList(actions) } + + assertThat(actions.last().map { it.viewKey }) + .isEqualTo( + listOf( + UserActionModel.ENTER_GUEST_MODE.ordinal.toLong(), + UserActionModel.ADD_USER.ordinal.toLong(), + UserActionModel.ADD_SUPERVISED_USER.ordinal.toLong(), + UserActionModel.NAVIGATE_TO_USER_MANAGEMENT.ordinal.toLong(), + ) ) - ) - job.cancel() - } + job.cancel() + } @Test - fun `isFinishRequested - finishes when user is switched`() = testScope.runTest { - val userInfos = setUsers(count = 2) - val isFinishRequested = mutableListOf<Boolean>() - val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } - assertThat(isFinishRequested.last()).isFalse() + fun `isFinishRequested - finishes when user is switched`() = + testScope.runTest { + val userInfos = setUsers(count = 2) + val isFinishRequested = mutableListOf<Boolean>() + val job = + launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } + assertThat(isFinishRequested.last()).isFalse() - userRepository.setSelectedUserInfo(userInfos[1]) + userRepository.setSelectedUserInfo(userInfos[1]) - assertThat(isFinishRequested.last()).isTrue() + assertThat(isFinishRequested.last()).isTrue() - job.cancel() - } + job.cancel() + } @Test - fun `isFinishRequested - finishes when the screen turns off`() = testScope.runTest { - setUsers(count = 2) - powerRepository.setInteractive(true) - val isFinishRequested = mutableListOf<Boolean>() - val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } - assertThat(isFinishRequested.last()).isFalse() + fun `isFinishRequested - finishes when the screen turns off`() = + testScope.runTest { + setUsers(count = 2) + powerRepository.setInteractive(true) + val isFinishRequested = mutableListOf<Boolean>() + val job = + launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } + assertThat(isFinishRequested.last()).isFalse() - powerRepository.setInteractive(false) + powerRepository.setInteractive(false) - assertThat(isFinishRequested.last()).isTrue() + assertThat(isFinishRequested.last()).isTrue() - job.cancel() - } + job.cancel() + } @Test - fun `isFinishRequested - finishes when cancel button is clicked`() = testScope.runTest { - setUsers(count = 2) - powerRepository.setInteractive(true) - val isFinishRequested = mutableListOf<Boolean>() - val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } - assertThat(isFinishRequested.last()).isFalse() + fun `isFinishRequested - finishes when cancel button is clicked`() = + testScope.runTest { + setUsers(count = 2) + powerRepository.setInteractive(true) + val isFinishRequested = mutableListOf<Boolean>() + val job = + launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } + assertThat(isFinishRequested.last()).isFalse() - underTest.onCancelButtonClicked() + underTest.onCancelButtonClicked() - assertThat(isFinishRequested.last()).isTrue() + assertThat(isFinishRequested.last()).isTrue() - underTest.onFinished() + underTest.onFinished() - assertThat(isFinishRequested.last()).isFalse() + assertThat(isFinishRequested.last()).isFalse() - job.cancel() - } + job.cancel() + } @Test - fun `guest selected -- name is exit guest`() = testScope.runTest { - val userInfos = + fun `guest selected -- name is exit guest`() = + testScope.runTest { + val userInfos = listOf( - UserInfo( - /* id= */ 0, - /* name= */ "zero", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - UserInfo( - /* id= */ 1, - /* name= */ "one", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_GUEST, - ), + UserInfo( + /* id= */ 0, + /* name= */ "zero", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_PRIMARY or + UserInfo.FLAG_ADMIN or + UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + UserInfo( + /* id= */ 1, + /* name= */ "one", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_GUEST, + ), ) - userRepository.setUserInfos(userInfos) - userRepository.setSelectedUserInfo(userInfos[1]) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[1]) - val userViewModels = mutableListOf<List<UserViewModel>>() - val job = launch(testDispatcher) { underTest.users.toList(userViewModels) } + val userViewModels = mutableListOf<List<UserViewModel>>() + val job = launch(testDispatcher) { underTest.users.toList(userViewModels) } - assertThat(userViewModels.last()).hasSize(2) - assertUserViewModel( + assertThat(userViewModels.last()).hasSize(2) + assertUserViewModel( viewModel = userViewModels.last()[0], viewKey = 0, name = Text.Loaded("zero"), isSelectionMarkerVisible = false, - ) - assertUserViewModel( + ) + assertUserViewModel( viewModel = userViewModels.last()[1], viewKey = 1, - name = Text.Resource( - com.android.settingslib.R.string.guest_exit_quick_settings_button - ), + name = + Text.Resource( + com.android.settingslib.R.string.guest_exit_quick_settings_button + ), isSelectionMarkerVisible = true, - ) - job.cancel() - } + ) + job.cancel() + } @Test - fun `guest not selected -- name is guest`() = testScope.runTest { - val userInfos = + fun `guest not selected -- name is guest`() = + testScope.runTest { + val userInfos = listOf( - UserInfo( - /* id= */ 0, - /* name= */ "zero", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - UserInfo( - /* id= */ 1, - /* name= */ "one", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_GUEST, - ), + UserInfo( + /* id= */ 0, + /* name= */ "zero", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_PRIMARY or + UserInfo.FLAG_ADMIN or + UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_SYSTEM, + ), + UserInfo( + /* id= */ 1, + /* name= */ "one", + /* iconPath= */ "", + /* flags= */ UserInfo.FLAG_FULL, + UserManager.USER_TYPE_FULL_GUEST, + ), ) - userRepository.setUserInfos(userInfos) - userRepository.setSelectedUserInfo(userInfos[0]) - runCurrent() + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + runCurrent() - val userViewModels = mutableListOf<List<UserViewModel>>() - val job = launch(testDispatcher) { underTest.users.toList(userViewModels) } + val userViewModels = mutableListOf<List<UserViewModel>>() + val job = launch(testDispatcher) { underTest.users.toList(userViewModels) } - assertThat(userViewModels.last()).hasSize(2) - assertUserViewModel( + assertThat(userViewModels.last()).hasSize(2) + assertUserViewModel( viewModel = userViewModels.last()[0], viewKey = 0, name = Text.Loaded("zero"), isSelectionMarkerVisible = true, - ) - assertUserViewModel( + ) + assertUserViewModel( viewModel = userViewModels.last()[1], viewKey = 1, name = Text.Loaded("one"), isSelectionMarkerVisible = false, - ) - job.cancel() - } + ) + job.cancel() + } private suspend fun setUsers(count: Int): List<UserInfo> { val userInfos = |