Refactor SysUI's interface with ATMS#setLockScreenShown/keyguardGoingAway.
Bug: 278086361 Test: atest SystemUITests Merged-In: Iaacccacbbc408bf5d39b32016b981199aa36dcc0 Change-Id: Iaacccacbbc408bf5d39b32016b981199aa36dcc0
This commit is contained in:
@@ -78,6 +78,7 @@ import com.android.systemui.classifier.FalsingCollector;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||
import com.android.systemui.log.SessionTracker;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
@@ -126,6 +127,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
|
||||
private final BouncerMessageInteractor mBouncerMessageInteractor;
|
||||
private int mTranslationY;
|
||||
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||
// Whether the volume keys should be handled by keyguard. If true, then
|
||||
// they will be handled here for specific media types such as music, otherwise
|
||||
// the audio service will bring up the volume dialog.
|
||||
@@ -299,6 +301,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
mViewMediatorCallback.keyguardDone(fromPrimaryAuth, targetUserId);
|
||||
}
|
||||
}
|
||||
|
||||
if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
mKeyguardTransitionInteractor.startDismissKeyguardTransition();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -424,6 +430,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
Provider<JavaAdapter> javaAdapter,
|
||||
UserInteractor userInteractor,
|
||||
FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
|
||||
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
||||
Provider<AuthenticationInteractor> authenticationInteractor
|
||||
) {
|
||||
super(view);
|
||||
@@ -455,6 +462,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
mUserInteractor = userInteractor;
|
||||
mAuthenticationInteractor = authenticationInteractor;
|
||||
mJavaAdapter = javaAdapter;
|
||||
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -302,6 +302,19 @@ object Flags {
|
||||
R.bool.flag_stop_pulsing_face_scanning_animation,
|
||||
"stop_pulsing_face_scanning_animation")
|
||||
|
||||
/**
|
||||
* TODO(b/278086361): Tracking bug
|
||||
* Complete rewrite of the interactions between System UI and Window Manager involving keyguard
|
||||
* state. When enabled, calls to ActivityTaskManagerService from System UI will exclusively
|
||||
* occur from [WmLockscreenVisibilityManager] rather than the legacy KeyguardViewMediator.
|
||||
*
|
||||
* This flag is under development; some types of unlock may not animate properly if you enable
|
||||
* it.
|
||||
*/
|
||||
@JvmField
|
||||
val KEYGUARD_WM_STATE_REFACTOR: UnreleasedFlag =
|
||||
unreleasedFlag("keyguard_wm_state_refactor")
|
||||
|
||||
// 300 - power menu
|
||||
// TODO(b/254512600): Tracking Bug
|
||||
@JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
|
||||
|
||||
@@ -73,6 +73,14 @@ import com.android.internal.policy.IKeyguardService;
|
||||
import com.android.internal.policy.IKeyguardStateCallback;
|
||||
import com.android.keyguard.mediator.ScreenOnCoordinator;
|
||||
import com.android.systemui.SystemUIApplication;
|
||||
import com.android.systemui.dagger.qualifiers.Application;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
|
||||
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
|
||||
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel;
|
||||
import com.android.systemui.settings.DisplayTracker;
|
||||
import com.android.wm.shell.transition.ShellTransitions;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
@@ -85,10 +93,13 @@ import java.util.WeakHashMap;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope;
|
||||
|
||||
public class KeyguardService extends Service {
|
||||
static final String TAG = "KeyguardService";
|
||||
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
|
||||
|
||||
private final FeatureFlags mFlags;
|
||||
private final KeyguardViewMediator mKeyguardViewMediator;
|
||||
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
|
||||
private final ScreenOnCoordinator mScreenOnCoordinator;
|
||||
@@ -291,13 +302,33 @@ public class KeyguardService extends Service {
|
||||
KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
|
||||
ScreenOnCoordinator screenOnCoordinator,
|
||||
ShellTransitions shellTransitions,
|
||||
DisplayTracker displayTracker) {
|
||||
DisplayTracker displayTracker,
|
||||
WindowManagerLockscreenVisibilityViewModel
|
||||
wmLockscreenVisibilityViewModel,
|
||||
WindowManagerLockscreenVisibilityManager wmLockscreenVisibilityManager,
|
||||
KeyguardSurfaceBehindViewModel keyguardSurfaceBehindViewModel,
|
||||
KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator,
|
||||
@Application CoroutineScope scope,
|
||||
FeatureFlags featureFlags) {
|
||||
super();
|
||||
mKeyguardViewMediator = keyguardViewMediator;
|
||||
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
|
||||
mScreenOnCoordinator = screenOnCoordinator;
|
||||
mShellTransitions = shellTransitions;
|
||||
mDisplayTracker = displayTracker;
|
||||
mFlags = featureFlags;
|
||||
|
||||
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
WindowManagerLockscreenVisibilityViewBinder.bind(
|
||||
wmLockscreenVisibilityViewModel,
|
||||
wmLockscreenVisibilityManager,
|
||||
scope);
|
||||
|
||||
KeyguardSurfaceBehindViewBinder.bind(
|
||||
keyguardSurfaceBehindViewModel,
|
||||
keyguardSurfaceBehindAnimator,
|
||||
scope);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -405,7 +405,9 @@ class KeyguardUnlockAnimationController @Inject constructor(
|
||||
* the device.
|
||||
*/
|
||||
fun canPerformInWindowLauncherAnimations(): Boolean {
|
||||
return isNexusLauncherUnderneath() &&
|
||||
// TODO(b/278086361): Refactor in-window animations.
|
||||
return !featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR) &&
|
||||
isNexusLauncherUnderneath() &&
|
||||
// If the launcher is underneath, but we're about to launch an activity, don't do
|
||||
// the animations since they won't be visible.
|
||||
!notificationShadeWindowController.isLaunchingActivity &&
|
||||
@@ -849,54 +851,57 @@ class KeyguardUnlockAnimationController @Inject constructor(
|
||||
}
|
||||
|
||||
surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget ->
|
||||
val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
|
||||
if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
val surfaceHeight: Int =
|
||||
surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
|
||||
|
||||
var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
|
||||
(1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
|
||||
MathUtils.clamp(amount, 0f, 1f))
|
||||
var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
|
||||
(1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
|
||||
MathUtils.clamp(amount, 0f, 1f))
|
||||
|
||||
// If we're dismissing via swipe to the Launcher, we'll play in-window scale animations,
|
||||
// so don't also scale the window.
|
||||
if (keyguardStateController.isDismissingFromSwipe &&
|
||||
willUnlockWithInWindowLauncherAnimations) {
|
||||
scaleFactor = 1f
|
||||
}
|
||||
|
||||
// Translate up from the bottom.
|
||||
surfaceBehindMatrix.setTranslate(
|
||||
surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
|
||||
surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() +
|
||||
surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
|
||||
)
|
||||
|
||||
// Scale up from a point at the center-bottom of the surface.
|
||||
surfaceBehindMatrix.postScale(
|
||||
scaleFactor,
|
||||
scaleFactor,
|
||||
keyguardViewController.viewRootImpl.width / 2f,
|
||||
surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
|
||||
)
|
||||
|
||||
// SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
|
||||
// unable to draw
|
||||
val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash
|
||||
if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
|
||||
sc?.isValid == true) {
|
||||
with(SurfaceControl.Transaction()) {
|
||||
setMatrix(sc, surfaceBehindMatrix, tmpFloat)
|
||||
setCornerRadius(sc, roundedCornerRadius)
|
||||
setAlpha(sc, animationAlpha)
|
||||
apply()
|
||||
// If we're dismissing via swipe to the Launcher, we'll play in-window scale
|
||||
// animations, so don't also scale the window.
|
||||
if (keyguardStateController.isDismissingFromSwipe &&
|
||||
willUnlockWithInWindowLauncherAnimations) {
|
||||
scaleFactor = 1f
|
||||
}
|
||||
} else {
|
||||
applyParamsToSurface(
|
||||
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
|
||||
surfaceBehindRemoteAnimationTarget.leash)
|
||||
.withMatrix(surfaceBehindMatrix)
|
||||
.withCornerRadius(roundedCornerRadius)
|
||||
.withAlpha(animationAlpha)
|
||||
.build()
|
||||
|
||||
// Translate up from the bottom.
|
||||
surfaceBehindMatrix.setTranslate(
|
||||
surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
|
||||
surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() +
|
||||
surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
|
||||
)
|
||||
|
||||
// Scale up from a point at the center-bottom of the surface.
|
||||
surfaceBehindMatrix.postScale(
|
||||
scaleFactor,
|
||||
scaleFactor,
|
||||
keyguardViewController.viewRootImpl.width / 2f,
|
||||
surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
|
||||
)
|
||||
|
||||
// SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
|
||||
// unable to draw
|
||||
val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash
|
||||
if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
|
||||
sc?.isValid == true) {
|
||||
with(SurfaceControl.Transaction()) {
|
||||
setMatrix(sc, surfaceBehindMatrix, tmpFloat)
|
||||
setCornerRadius(sc, roundedCornerRadius)
|
||||
setAlpha(sc, animationAlpha)
|
||||
apply()
|
||||
}
|
||||
} else {
|
||||
applyParamsToSurface(
|
||||
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
|
||||
surfaceBehindRemoteAnimationTarget.leash)
|
||||
.withMatrix(surfaceBehindMatrix)
|
||||
.withCornerRadius(roundedCornerRadius)
|
||||
.withAlpha(animationAlpha)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,10 +990,12 @@ class KeyguardUnlockAnimationController @Inject constructor(
|
||||
if (keyguardStateController.isShowing) {
|
||||
// Hide the keyguard, with no fade out since we animated it away during the unlock.
|
||||
|
||||
keyguardViewController.hide(
|
||||
surfaceBehindRemoteAnimationStartTime,
|
||||
0 /* fadeOutDuration */
|
||||
)
|
||||
if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
keyguardViewController.hide(
|
||||
surfaceBehindRemoteAnimationStartTime,
|
||||
0 /* fadeOutDuration */
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " +
|
||||
"showing. Ignoring...")
|
||||
|
||||
@@ -171,8 +171,6 @@ import com.android.systemui.util.time.SystemClock;
|
||||
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
|
||||
import com.android.wm.shell.keyguard.KeyguardTransitions;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -182,6 +180,7 @@ import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import dagger.Lazy;
|
||||
import kotlinx.coroutines.CoroutineDispatcher;
|
||||
|
||||
/**
|
||||
@@ -1035,12 +1034,19 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
Trace.beginSection("mExitAnimationRunner.onAnimationStart#startKeyguardExitAnimation");
|
||||
startKeyguardExitAnimation(transit, apps, wallpapers, nonApps, finishedCallback);
|
||||
if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
mWmLockscreenVisibilityManager.get().onKeyguardGoingAwayRemoteAnimationStart(
|
||||
transit, apps, wallpapers, nonApps, finishedCallback);
|
||||
}
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
@Override // Binder interface
|
||||
public void onAnimationCancelled() {
|
||||
cancelKeyguardExitAnimation();
|
||||
if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
mWmLockscreenVisibilityManager.get().onKeyguardGoingAwayRemoteAnimationCancelled();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1106,7 +1112,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
|
||||
mOccludeByDreamAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||
mOccludeByDreamAnimator.setDuration(mDreamOpenAnimationDuration);
|
||||
mOccludeByDreamAnimator.setInterpolator(Interpolators.LINEAR);
|
||||
//mOccludeByDreamAnimator.setInterpolator(Interpolators.LINEAR);
|
||||
mOccludeByDreamAnimator.addUpdateListener(
|
||||
animation -> {
|
||||
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
|
||||
@@ -1335,6 +1341,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
mDreamingToLockscreenTransitionViewModel;
|
||||
private RemoteAnimationTarget mRemoteAnimationTarget;
|
||||
|
||||
private Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager;
|
||||
|
||||
/**
|
||||
* Injected constructor. See {@link KeyguardModule}.
|
||||
*/
|
||||
@@ -1377,7 +1385,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
SystemClock systemClock,
|
||||
@Main CoroutineDispatcher mainDispatcher,
|
||||
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
|
||||
SystemPropertiesHelper systemPropertiesHelper) {
|
||||
SystemPropertiesHelper systemPropertiesHelper,
|
||||
Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager) {
|
||||
mContext = context;
|
||||
mUserTracker = userTracker;
|
||||
mFalsingCollector = falsingCollector;
|
||||
@@ -1443,8 +1452,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mSessionTracker = sessionTracker;
|
||||
|
||||
mMainDispatcher = mainDispatcher;
|
||||
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
|
||||
mWmLockscreenVisibilityManager = wmLockscreenVisibilityManager;
|
||||
mMainDispatcher = mainDispatcher;
|
||||
}
|
||||
|
||||
public void userActivity() {
|
||||
@@ -2677,6 +2687,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
|
||||
}
|
||||
|
||||
if (mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// Handled in WmLockscreenVisibilityManager if flag is enabled.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing);
|
||||
} catch (RemoteException e) {
|
||||
@@ -2716,7 +2732,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
}
|
||||
mHiding = false;
|
||||
|
||||
mKeyguardViewControllerLazy.get().show(options);
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// Handled directly in StatusBarKeyguardViewManager if enabled.
|
||||
mKeyguardViewControllerLazy.get().show(options);
|
||||
}
|
||||
|
||||
resetKeyguardDonePendingLocked();
|
||||
mHideAnimationRun = false;
|
||||
adjustStatusBarLocked();
|
||||
@@ -2787,19 +2807,22 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
mUpdateMonitor.setKeyguardGoingAway(true);
|
||||
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(true);
|
||||
|
||||
// Don't actually hide the Keyguard at the moment, wait for window
|
||||
// manager until it tells us it's safe to do so with
|
||||
// startKeyguardExitAnimation.
|
||||
// Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager will be in
|
||||
// order.
|
||||
final int keyguardFlag = flags;
|
||||
mUiBgExecutor.execute(() -> {
|
||||
try {
|
||||
ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error while calling WindowManager", e);
|
||||
}
|
||||
});
|
||||
// Handled in WmLockscreenVisibilityManager if flag is enabled.
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// Don't actually hide the Keyguard at the moment, wait for window manager until it
|
||||
// tells us it's safe to do so with startKeyguardExitAnimation.
|
||||
// Posting to mUiOffloadThread to ensure that calls to ActivityTaskManager will be
|
||||
// in order.
|
||||
final int keyguardFlag = flags;
|
||||
mUiBgExecutor.execute(() -> {
|
||||
try {
|
||||
ActivityTaskManager.getService().keyguardGoingAway(keyguardFlag);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error while calling WindowManager", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Trace.endSection();
|
||||
}
|
||||
};
|
||||
@@ -2913,7 +2936,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
if (!mHiding
|
||||
&& !mSurfaceBehindRemoteAnimationRequested
|
||||
&& !mKeyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture()) {
|
||||
if (finishedCallback != null) {
|
||||
// If the flag is enabled, remote animation state is handled in
|
||||
// WmLockscreenVisibilityManager.
|
||||
if (finishedCallback != null
|
||||
&& !mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// There will not execute animation, send a finish callback to ensure the remote
|
||||
// animation won't hang there.
|
||||
try {
|
||||
@@ -2939,10 +2965,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
new IRemoteAnimationFinishedCallback() {
|
||||
@Override
|
||||
public void onAnimationFinished() throws RemoteException {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to call onAnimationFinished", e);
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to call onAnimationFinished", e);
|
||||
}
|
||||
}
|
||||
onKeyguardExitFinished();
|
||||
mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
|
||||
@@ -2969,7 +2997,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
// it will dismiss the panel in that case.
|
||||
} else if (!mStatusBarStateController.leaveOpenOnKeyguardHide()
|
||||
&& apps != null && apps.length > 0) {
|
||||
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// Handled in WmLockscreenVisibilityManager. Other logic in this class will
|
||||
// short circuit when this is null.
|
||||
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
|
||||
}
|
||||
mSurfaceBehindRemoteAnimationRunning = true;
|
||||
|
||||
mInteractionJankMonitor.begin(
|
||||
@@ -2989,7 +3021,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
createInteractionJankMonitorConf(
|
||||
CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RemoteAnimationDisabled"));
|
||||
|
||||
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// Handled directly in StatusBarKeyguardViewManager if enabled.
|
||||
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
|
||||
}
|
||||
|
||||
// TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
|
||||
// apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
|
||||
@@ -3003,13 +3038,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
}
|
||||
if (apps == null || apps.length == 0) {
|
||||
Slog.e(TAG, "Keyguard exit without a corresponding app to show.");
|
||||
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
finishedCallback.onAnimationFinished();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
} finally {
|
||||
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3033,7 +3072,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
finishedCallback.onAnimationFinished();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
} finally {
|
||||
@@ -3044,7 +3085,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
finishedCallback.onAnimationFinished();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "RemoteException");
|
||||
} finally {
|
||||
@@ -3193,7 +3236,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
|
||||
}
|
||||
|
||||
ActivityTaskManager.getService().keyguardGoingAway(flags);
|
||||
if (!mFeatureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// Handled in WmLockscreenVisibilityManager.
|
||||
ActivityTaskManager.getService().keyguardGoingAway(flags);
|
||||
}
|
||||
mKeyguardStateController.notifyKeyguardGoingAway(true);
|
||||
} catch (RemoteException e) {
|
||||
mSurfaceBehindRemoteAnimationRequested = false;
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.app.IActivityTaskManager
|
||||
import android.util.Log
|
||||
import android.view.IRemoteAnimationFinishedCallback
|
||||
import android.view.RemoteAnimationTarget
|
||||
import android.view.WindowManager
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Manages lockscreen and AOD visibility state via the [IActivityTaskManager], and keeps track of
|
||||
* remote animations related to changes in lockscreen visibility.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class WindowManagerLockscreenVisibilityManager
|
||||
@Inject
|
||||
constructor(
|
||||
@Main private val executor: Executor,
|
||||
private val activityTaskManagerService: IActivityTaskManager,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Whether the lockscreen is showing, which we pass to [IActivityTaskManager.setLockScreenShown]
|
||||
* in order to show the lockscreen and hide the surface behind the keyguard (or the inverse).
|
||||
*/
|
||||
private var isLockscreenShowing = true
|
||||
|
||||
/**
|
||||
* Whether AOD is showing, which we pass to [IActivityTaskManager.setLockScreenShown] in order
|
||||
* to show AOD when the lockscreen is visible.
|
||||
*/
|
||||
private var isAodVisible = false
|
||||
|
||||
/**
|
||||
* Whether the keyguard is currently "going away", which we triggered via a call to
|
||||
* [IActivityTaskManager.keyguardGoingAway]. When we tell WM that the keyguard is going away,
|
||||
* the app/launcher surface behind the keyguard is made visible, and WM calls
|
||||
* [onKeyguardGoingAwayRemoteAnimationStart] with a RemoteAnimationTarget so that we can animate
|
||||
* it.
|
||||
*
|
||||
* Going away does not inherently result in [isLockscreenShowing] being set to false; we need to
|
||||
* do that ourselves once we are done animating the surface.
|
||||
*
|
||||
* THIS IS THE ONLY PLACE 'GOING AWAY' TERMINOLOGY SHOULD BE USED. 'Going away' is a WM concept
|
||||
* and we have gotten into trouble using it to mean various different things in the past. Unlock
|
||||
* animations may still be visible when the keyguard is NOT 'going away', for example, when we
|
||||
* play in-window animations, we set the surface to alpha=1f and end the animation immediately.
|
||||
* The remainder of the animation occurs in-window, so while you might expect that the keyguard
|
||||
* is still 'going away' because unlock animations are playing, it's actually not.
|
||||
*
|
||||
* If you want to know if the keyguard is 'going away', you probably want to check if we have
|
||||
* STARTED but not FINISHED a transition to GONE.
|
||||
*
|
||||
* The going away animation will run until:
|
||||
* - We manually call [endKeyguardGoingAwayAnimation] after we're done animating.
|
||||
* - We call [setLockscreenShown] = true, which cancels the going away animation.
|
||||
* - WM calls [onKeyguardGoingAwayRemoteAnimationCancelled] for another reason (such as the 10
|
||||
* second timeout).
|
||||
*/
|
||||
private var isKeyguardGoingAway = false
|
||||
private set(value) {
|
||||
// TODO(b/278086361): Extricate the keyguard state controller.
|
||||
keyguardStateController.notifyKeyguardGoingAway(value)
|
||||
field = value
|
||||
}
|
||||
|
||||
/** Callback provided by WM to call once we're done with the going away animation. */
|
||||
private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
|
||||
|
||||
/**
|
||||
* Set the visibility of the surface behind the keyguard, making the appropriate calls to Window
|
||||
* Manager to effect the change.
|
||||
*/
|
||||
fun setSurfaceBehindVisibility(visible: Boolean) {
|
||||
if (isKeyguardGoingAway == visible) {
|
||||
Log.d(TAG, "WmLockscreenVisibilityManager#setVisibility -> already visible=$visible")
|
||||
return
|
||||
}
|
||||
|
||||
// The surface behind is always visible if the lockscreen is not showing, so we're already
|
||||
// visible.
|
||||
if (visible && !isLockscreenShowing) {
|
||||
Log.d(TAG, "#setVisibility -> already visible since the lockscreen isn't showing")
|
||||
return
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
// Make the surface visible behind the keyguard by calling keyguardGoingAway. The
|
||||
// lockscreen is still showing as well, allowing us to animate unlocked.
|
||||
Log.d(TAG, "ActivityTaskManagerService#keyguardGoingAway()")
|
||||
activityTaskManagerService.keyguardGoingAway(0)
|
||||
isKeyguardGoingAway = true
|
||||
} else {
|
||||
// Hide the surface by setting the lockscreen showing.
|
||||
setLockscreenShown(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun setAodVisible(aodVisible: Boolean) {
|
||||
setWmLockscreenState(aodVisible = aodVisible)
|
||||
}
|
||||
|
||||
/** Sets the visibility of the lockscreen. */
|
||||
fun setLockscreenShown(lockscreenShown: Boolean) {
|
||||
setWmLockscreenState(lockscreenShowing = lockscreenShown)
|
||||
}
|
||||
|
||||
fun onKeyguardGoingAwayRemoteAnimationStart(
|
||||
@WindowManager.TransitionOldType transit: Int,
|
||||
apps: Array<RemoteAnimationTarget>,
|
||||
wallpapers: Array<RemoteAnimationTarget>,
|
||||
nonApps: Array<RemoteAnimationTarget>,
|
||||
finishedCallback: IRemoteAnimationFinishedCallback
|
||||
) {
|
||||
goingAwayRemoteAnimationFinishedCallback = finishedCallback
|
||||
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
|
||||
}
|
||||
|
||||
fun onKeyguardGoingAwayRemoteAnimationCancelled() {
|
||||
// If WM cancelled the animation, we need to end immediately even if we're still using the
|
||||
// animation.
|
||||
endKeyguardGoingAwayAnimation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the going away remote animation target is in-use, which means we're animating it or
|
||||
* intend to animate it.
|
||||
*
|
||||
* Some unlock animations (such as the translation spring animation) are non-deterministic and
|
||||
* might end after the transition to GONE ends. In that case, we want to keep the remote
|
||||
* animation running until the spring ends.
|
||||
*/
|
||||
fun setUsingGoingAwayRemoteAnimation(usingTarget: Boolean) {
|
||||
if (!usingTarget) {
|
||||
endKeyguardGoingAwayAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setWmLockscreenState(
|
||||
lockscreenShowing: Boolean = this.isLockscreenShowing,
|
||||
aodVisible: Boolean = this.isAodVisible
|
||||
) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"#setWmLockscreenState(" +
|
||||
"isLockscreenShowing=$lockscreenShowing, " +
|
||||
"aodVisible=$aodVisible)."
|
||||
)
|
||||
|
||||
if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
|
||||
return
|
||||
}
|
||||
|
||||
activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
|
||||
this.isLockscreenShowing = lockscreenShowing
|
||||
this.isAodVisible = aodVisible
|
||||
}
|
||||
|
||||
private fun endKeyguardGoingAwayAnimation() {
|
||||
if (!isKeyguardGoingAway) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"#endKeyguardGoingAwayAnimation() called when isKeyguardGoingAway=false. " +
|
||||
"Short-circuiting."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
executor.execute {
|
||||
Log.d(TAG, "Finishing remote animation.")
|
||||
goingAwayRemoteAnimationFinishedCallback?.onAnimationFinished()
|
||||
goingAwayRemoteAnimationFinishedCallback = null
|
||||
|
||||
isKeyguardGoingAway = false
|
||||
|
||||
keyguardSurfaceBehindAnimator.notifySurfaceReleased()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = this::class.java.simpleName
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ import com.android.systemui.flags.SystemPropertiesHelper;
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager;
|
||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule;
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
|
||||
@@ -73,12 +74,11 @@ import com.android.systemui.util.time.SystemClock;
|
||||
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
|
||||
import com.android.wm.shell.keyguard.KeyguardTransitions;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher;
|
||||
|
||||
/**
|
||||
@@ -144,7 +144,8 @@ public class KeyguardModule {
|
||||
SystemClock systemClock,
|
||||
@Main CoroutineDispatcher mainDispatcher,
|
||||
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
|
||||
SystemPropertiesHelper systemPropertiesHelper) {
|
||||
SystemPropertiesHelper systemPropertiesHelper,
|
||||
Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager) {
|
||||
return new KeyguardViewMediator(
|
||||
context,
|
||||
uiEventLogger,
|
||||
@@ -186,7 +187,8 @@ public class KeyguardModule {
|
||||
systemClock,
|
||||
mainDispatcher,
|
||||
dreamingToLockscreenTransitionViewModel,
|
||||
systemPropertiesHelper);
|
||||
systemPropertiesHelper,
|
||||
wmLockscreenVisibilityManager);
|
||||
}
|
||||
|
||||
/** */
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
|
||||
import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
|
||||
import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
|
||||
import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||
import com.android.systemui.log.FaceAuthenticationLogger
|
||||
@@ -160,7 +161,7 @@ constructor(
|
||||
@FaceDetectTableLog private val faceDetectLog: TableLogBuffer,
|
||||
@FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
|
||||
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
|
||||
featureFlags: FeatureFlags,
|
||||
private val featureFlags: FeatureFlags,
|
||||
facePropertyRepository: FacePropertyRepository,
|
||||
dumpManager: DumpManager,
|
||||
) : DeviceEntryFaceAuthRepository, Dumpable {
|
||||
@@ -286,8 +287,12 @@ constructor(
|
||||
// starts going to sleep.
|
||||
merge(
|
||||
keyguardRepository.wakefulness.map { it.isStartingToSleepOrAsleep() },
|
||||
keyguardRepository.isKeyguardGoingAway,
|
||||
userRepository.userSwitchingInProgress
|
||||
if (featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE)
|
||||
} else {
|
||||
keyguardRepository.isKeyguardGoingAway
|
||||
},
|
||||
userRepository.userSwitchingInProgress,
|
||||
)
|
||||
.onEach { anyOfThemIsTrue ->
|
||||
if (anyOfThemIsTrue) {
|
||||
|
||||
@@ -99,7 +99,16 @@ interface KeyguardRepository {
|
||||
/** Is an activity showing over the keyguard? */
|
||||
val isKeyguardOccluded: Flow<Boolean>
|
||||
|
||||
/** Observable for the signal that keyguard is about to go away. */
|
||||
/**
|
||||
* Observable for the signal that keyguard is about to go away.
|
||||
*
|
||||
* TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed.
|
||||
*/
|
||||
@Deprecated(
|
||||
"Use KeyguardTransitionInteractor flows instead. The closest match for 'going " +
|
||||
"away' is isInTransitionToState(GONE), but consider using more specific flows " +
|
||||
"whenever possible."
|
||||
)
|
||||
val isKeyguardGoingAway: Flow<Boolean>
|
||||
|
||||
/** Is the always-on display available to be used? */
|
||||
@@ -365,10 +374,11 @@ constructor(
|
||||
|
||||
awaitClose { keyguardStateController.removeCallback(callback) }
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = keyguardStateController.isUnlocked,
|
||||
scope,
|
||||
SharingStarted.Eagerly,
|
||||
initialValue = false,
|
||||
)
|
||||
|
||||
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
|
||||
|
||||
@@ -31,6 +31,11 @@ import dagger.multibindings.IntoMap
|
||||
interface KeyguardRepositoryModule {
|
||||
@Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
|
||||
|
||||
@Binds
|
||||
fun keyguardSurfaceBehindRepository(
|
||||
impl: KeyguardSurfaceBehindRepositoryImpl
|
||||
): KeyguardSurfaceBehindRepository
|
||||
|
||||
@Binds
|
||||
fun keyguardTransitionRepository(
|
||||
impl: KeyguardTransitionRepositoryImpl
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.data.repository
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* State related to SysUI's handling of the surface behind the keyguard (typically an app or the
|
||||
* launcher). We manipulate this surface during unlock animations.
|
||||
*/
|
||||
interface KeyguardSurfaceBehindRepository {
|
||||
|
||||
/** Whether we're running animations on the surface. */
|
||||
val isAnimatingSurface: Flow<Boolean>
|
||||
|
||||
/** Set whether we're running animations on the surface. */
|
||||
fun setAnimatingSurface(animating: Boolean)
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
class KeyguardSurfaceBehindRepositoryImpl @Inject constructor() : KeyguardSurfaceBehindRepository {
|
||||
private val _isAnimatingSurface = MutableStateFlow(false)
|
||||
override val isAnimatingSurface = _isAnimatingSurface.asStateFlow()
|
||||
|
||||
override fun setAnimatingSurface(animating: Boolean) {
|
||||
_isAnimatingSurface.value = animating
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import com.android.app.animation.Interpolators
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
@@ -26,6 +25,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessState
|
||||
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
|
||||
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
|
||||
import com.android.systemui.util.kotlin.sample
|
||||
import com.android.wm.shell.animation.Interpolators
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@@ -20,8 +20,11 @@ import android.animation.ValueAnimator
|
||||
import com.android.app.animation.Interpolators
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
|
||||
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
|
||||
import com.android.systemui.keyguard.shared.model.TransitionInfo
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||
@@ -34,7 +37,11 @@ import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SysUISingleton
|
||||
@@ -45,6 +52,7 @@ constructor(
|
||||
override val transitionInteractor: KeyguardTransitionInteractor,
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val keyguardInteractor: KeyguardInteractor,
|
||||
private val flags: FeatureFlags,
|
||||
private val shadeRepository: ShadeRepository,
|
||||
) :
|
||||
TransitionInteractor(
|
||||
@@ -53,6 +61,7 @@ constructor(
|
||||
|
||||
override fun start() {
|
||||
listenForLockscreenToGone()
|
||||
listenForLockscreenToGoneDragging()
|
||||
listenForLockscreenToOccluded()
|
||||
listenForLockscreenToCamera()
|
||||
listenForLockscreenToAodOrDozing()
|
||||
@@ -62,6 +71,63 @@ constructor(
|
||||
listenForLockscreenToAlternateBouncer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we want the surface behind the keyguard visible for the transition from LOCKSCREEN,
|
||||
* or null if we don't care and should just use a reasonable default.
|
||||
*
|
||||
* [KeyguardSurfaceBehindInteractor] will switch to this flow whenever a transition from
|
||||
* LOCKSCREEN is running.
|
||||
*/
|
||||
val surfaceBehindVisibility: Flow<Boolean?> =
|
||||
transitionInteractor.startedKeyguardTransitionStep
|
||||
.map { startedStep ->
|
||||
if (startedStep.to != KeyguardState.GONE) {
|
||||
// LOCKSCREEN to anything but GONE does not require any special surface
|
||||
// visibility handling.
|
||||
return@map null
|
||||
}
|
||||
|
||||
true // TODO(b/278086361): Implement continuous swipe to unlock.
|
||||
}
|
||||
.onStart {
|
||||
// Default to null ("don't care, use a reasonable default").
|
||||
emit(null)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
/**
|
||||
* The surface behind view params to use for the transition from LOCKSCREEN, or null if we don't
|
||||
* care and should use a reasonable default.
|
||||
*/
|
||||
val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
|
||||
combine(
|
||||
transitionInteractor.startedKeyguardTransitionStep,
|
||||
transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN)
|
||||
) { startedStep, fromLockscreenStep ->
|
||||
if (startedStep.to != KeyguardState.GONE) {
|
||||
// Only LOCKSCREEN -> GONE has specific surface params (for the unlock
|
||||
// animation).
|
||||
return@combine null
|
||||
} else if (fromLockscreenStep.value > 0.5f) {
|
||||
// Start the animation once we're 50% transitioned to GONE.
|
||||
KeyguardSurfaceBehindModel(
|
||||
animateFromAlpha = 0f,
|
||||
alpha = 1f,
|
||||
animateFromTranslationY = 500f,
|
||||
translationY = 0f
|
||||
)
|
||||
} else {
|
||||
KeyguardSurfaceBehindModel(
|
||||
alpha = 0f,
|
||||
)
|
||||
}
|
||||
}
|
||||
.onStart {
|
||||
// Default to null ("don't care, use a reasonable default").
|
||||
emit(null)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
private fun listenForLockscreenToDreaming() {
|
||||
val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING)
|
||||
scope.launch {
|
||||
@@ -169,7 +235,8 @@ constructor(
|
||||
}
|
||||
|
||||
// If canceled, just put the state back
|
||||
// TODO: This logic should happen in FromPrimaryBouncerInteractor.
|
||||
// TODO(b/278086361): This logic should happen in
|
||||
// FromPrimaryBouncerInteractor.
|
||||
if (nextState == TransitionState.CANCELED) {
|
||||
transitionRepository.startTransition(
|
||||
TransitionInfo(
|
||||
@@ -201,7 +268,32 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissKeyguard() {
|
||||
startTransitionTo(KeyguardState.GONE)
|
||||
}
|
||||
|
||||
private fun listenForLockscreenToGone() {
|
||||
if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
keyguardInteractor.isKeyguardGoingAway
|
||||
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
|
||||
.collect { pair ->
|
||||
val (isKeyguardGoingAway, lastStartedStep) = pair
|
||||
if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) {
|
||||
startTransitionTo(KeyguardState.GONE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun listenForLockscreenToGoneDragging() {
|
||||
if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
keyguardInteractor.isKeyguardGoingAway
|
||||
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
|
||||
@@ -291,7 +383,7 @@ constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_DURATION = 500.milliseconds
|
||||
private val DEFAULT_DURATION = 400.milliseconds
|
||||
val TO_DREAMING_DURATION = 933.milliseconds
|
||||
val TO_OCCLUDED_DURATION = 450.milliseconds
|
||||
}
|
||||
|
||||
@@ -17,23 +17,28 @@
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import com.android.app.animation.Interpolators
|
||||
import com.android.keyguard.KeyguardSecurityModel
|
||||
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
|
||||
import com.android.systemui.keyguard.shared.model.WakefulnessState
|
||||
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
|
||||
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
|
||||
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
|
||||
import com.android.systemui.util.kotlin.sample
|
||||
import com.android.wm.shell.animation.Interpolators
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SysUISingleton
|
||||
@@ -44,6 +49,7 @@ constructor(
|
||||
override val transitionInteractor: KeyguardTransitionInteractor,
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val keyguardInteractor: KeyguardInteractor,
|
||||
private val flags: FeatureFlags,
|
||||
private val keyguardSecurityModel: KeyguardSecurityModel,
|
||||
) :
|
||||
TransitionInteractor(
|
||||
@@ -57,6 +63,57 @@ constructor(
|
||||
listenForPrimaryBouncerToDreamingLockscreenHosted()
|
||||
}
|
||||
|
||||
val surfaceBehindVisibility: Flow<Boolean?> =
|
||||
combine(
|
||||
transitionInteractor.startedKeyguardTransitionStep,
|
||||
transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER)
|
||||
) { startedStep, fromBouncerStep ->
|
||||
if (startedStep.to != KeyguardState.GONE) {
|
||||
return@combine null
|
||||
}
|
||||
|
||||
fromBouncerStep.value > 0.5f
|
||||
}
|
||||
.onStart {
|
||||
// Default to null ("don't care, use a reasonable default").
|
||||
emit(null)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
|
||||
combine(
|
||||
transitionInteractor.startedKeyguardTransitionStep,
|
||||
transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER)
|
||||
) { startedStep, fromBouncerStep ->
|
||||
if (startedStep.to != KeyguardState.GONE) {
|
||||
// BOUNCER to anything but GONE does not require any special surface
|
||||
// visibility handling.
|
||||
return@combine null
|
||||
}
|
||||
|
||||
if (fromBouncerStep.value > 0.5f) {
|
||||
KeyguardSurfaceBehindModel(
|
||||
animateFromAlpha = 0f,
|
||||
alpha = 1f,
|
||||
animateFromTranslationY = 500f,
|
||||
translationY = 0f,
|
||||
)
|
||||
} else {
|
||||
KeyguardSurfaceBehindModel(
|
||||
alpha = 0f,
|
||||
)
|
||||
}
|
||||
}
|
||||
.onStart {
|
||||
// Default to null ("don't care, use a reasonable default").
|
||||
emit(null)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
fun dismissPrimaryBouncer() {
|
||||
startTransitionTo(KeyguardState.GONE)
|
||||
}
|
||||
|
||||
private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
|
||||
scope.launch {
|
||||
keyguardInteractor.primaryBouncerShowing
|
||||
@@ -124,28 +181,34 @@ constructor(
|
||||
private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
|
||||
scope.launch {
|
||||
keyguardInteractor.primaryBouncerShowing
|
||||
.sample(
|
||||
combine(
|
||||
keyguardInteractor.isActiveDreamLockscreenHosted,
|
||||
transitionInteractor.startedKeyguardTransitionStep,
|
||||
::Pair
|
||||
),
|
||||
::toTriple
|
||||
)
|
||||
.collect {
|
||||
(isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
|
||||
if (
|
||||
!isBouncerShowing &&
|
||||
isActiveDreamLockscreenHosted &&
|
||||
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
|
||||
) {
|
||||
startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
|
||||
.sample(
|
||||
combine(
|
||||
keyguardInteractor.isActiveDreamLockscreenHosted,
|
||||
transitionInteractor.startedKeyguardTransitionStep,
|
||||
::Pair
|
||||
),
|
||||
::toTriple
|
||||
)
|
||||
.collect { (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
|
||||
if (
|
||||
!isBouncerShowing &&
|
||||
isActiveDreamLockscreenHosted &&
|
||||
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
|
||||
) {
|
||||
startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun listenForPrimaryBouncerToGone() {
|
||||
if (flags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
// This is handled in KeyguardSecurityContainerController and
|
||||
// StatusBarKeyguardViewManager, which calls the transition interactor to kick off a
|
||||
// transition vs. listening to legacy state flags.
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
keyguardInteractor.isKeyguardGoingAway
|
||||
.sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
|
||||
@@ -160,7 +223,7 @@ constructor(
|
||||
)
|
||||
// IME for password requires a slightly faster animation
|
||||
val duration =
|
||||
if (securityMode == Password) {
|
||||
if (securityMode == KeyguardSecurityModel.SecurityMode.Password) {
|
||||
TO_GONE_SHORT_DURATION
|
||||
} else {
|
||||
TO_GONE_DURATION
|
||||
@@ -188,7 +251,7 @@ constructor(
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_DURATION = 300.milliseconds
|
||||
val TO_GONE_DURATION = 250.milliseconds
|
||||
val TO_GONE_DURATION = 500.milliseconds
|
||||
val TO_GONE_SHORT_DURATION = 200.milliseconds
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ 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.KeyguardRootViewVisibilityState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.ScreenModel
|
||||
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.util.kotlin.sample
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -53,6 +53,7 @@ import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
|
||||
@@ -264,7 +265,26 @@ constructor(
|
||||
repository.setAnimateDozingTransitions(animate)
|
||||
}
|
||||
|
||||
fun isKeyguardDismissable(): Boolean {
|
||||
return repository.isKeyguardUnlocked.value
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KeyguardInteractor"
|
||||
|
||||
fun isKeyguardVisibleInState(state: KeyguardState): Boolean {
|
||||
return when (state) {
|
||||
KeyguardState.OFF -> true
|
||||
KeyguardState.DOZING -> true
|
||||
KeyguardState.DREAMING -> true
|
||||
KeyguardState.AOD -> true
|
||||
KeyguardState.ALTERNATE_BOUNCER -> true
|
||||
KeyguardState.PRIMARY_BOUNCER -> true
|
||||
KeyguardState.LOCKSCREEN -> true
|
||||
KeyguardState.GONE -> false
|
||||
KeyguardState.OCCLUDED -> true
|
||||
KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
@SysUISingleton
|
||||
class KeyguardSurfaceBehindInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
private val repository: KeyguardSurfaceBehindRepository,
|
||||
private val fromLockscreenInteractor: FromLockscreenTransitionInteractor,
|
||||
private val fromPrimaryBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val viewParams: Flow<KeyguardSurfaceBehindModel> =
|
||||
transitionInteractor.isInTransitionToAnyState
|
||||
.flatMapLatest { isInTransition ->
|
||||
if (!isInTransition) {
|
||||
defaultParams
|
||||
} else {
|
||||
combine(
|
||||
transitionSpecificViewParams,
|
||||
defaultParams,
|
||||
) { transitionParams, defaultParams ->
|
||||
transitionParams ?: defaultParams
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isAnimatingSurface = repository.isAnimatingSurface
|
||||
|
||||
private val defaultParams =
|
||||
transitionInteractor.finishedKeyguardState.map { state ->
|
||||
KeyguardSurfaceBehindModel(
|
||||
alpha =
|
||||
if (WindowManagerLockscreenVisibilityInteractor.isSurfaceVisible(state)) 1f
|
||||
else 0f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* View params provided by the transition interactor for the most recently STARTED transition.
|
||||
* This is used to run transition-specific animations on the surface.
|
||||
*
|
||||
* If null, there are no transition-specific view params needed for this transition and we will
|
||||
* use a reasonable default.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val transitionSpecificViewParams: Flow<KeyguardSurfaceBehindModel?> =
|
||||
transitionInteractor.startedKeyguardTransitionStep.flatMapLatest { startedStep ->
|
||||
when (startedStep.from) {
|
||||
KeyguardState.LOCKSCREEN -> fromLockscreenInteractor.surfaceBehindModel
|
||||
KeyguardState.PRIMARY_BOUNCER -> fromPrimaryBouncerInteractor.surfaceBehindModel
|
||||
// Return null for other states, where no transition specific params are needed.
|
||||
else -> flowOf(null)
|
||||
}
|
||||
}
|
||||
|
||||
fun setAnimatingSurface(animating: Boolean) {
|
||||
repository.setAnimatingSurface(animating)
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,20 @@
|
||||
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.util.Log
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
@@ -36,6 +39,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
@@ -46,8 +50,12 @@ import kotlinx.coroutines.flow.stateIn
|
||||
class KeyguardTransitionInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
private val repository: KeyguardTransitionRepository,
|
||||
@Application val scope: CoroutineScope,
|
||||
private val repository: KeyguardTransitionRepository,
|
||||
private val keyguardInteractor: dagger.Lazy<KeyguardInteractor>,
|
||||
private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
|
||||
private val fromPrimaryBouncerTransitionInteractor:
|
||||
dagger.Lazy<FromPrimaryBouncerTransitionInteractor>,
|
||||
) {
|
||||
private val TAG = this::class.simpleName
|
||||
|
||||
@@ -128,12 +136,11 @@ constructor(
|
||||
repository.transition(PRIMARY_BOUNCER, GONE)
|
||||
|
||||
/** OFF->LOCKSCREEN transition information. */
|
||||
val offToLockscreenTransition: Flow<TransitionStep> =
|
||||
repository.transition(KeyguardState.OFF, LOCKSCREEN)
|
||||
val offToLockscreenTransition: Flow<TransitionStep> = repository.transition(OFF, LOCKSCREEN)
|
||||
|
||||
/** DOZING->LOCKSCREEN transition information. */
|
||||
val dozingToLockscreenTransition: Flow<TransitionStep> =
|
||||
repository.transition(KeyguardState.DOZING, LOCKSCREEN)
|
||||
repository.transition(DOZING, LOCKSCREEN)
|
||||
|
||||
/**
|
||||
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
|
||||
@@ -157,17 +164,30 @@ constructor(
|
||||
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
|
||||
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
|
||||
|
||||
/** The destination state of the last started transition */
|
||||
/** The destination state of the last started transition. */
|
||||
val startedKeyguardState: StateFlow<KeyguardState> =
|
||||
startedKeyguardTransitionStep
|
||||
.map { step -> step.to }
|
||||
.stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF)
|
||||
.stateIn(scope, SharingStarted.Eagerly, OFF)
|
||||
|
||||
/** The last completed [KeyguardState] transition */
|
||||
val finishedKeyguardState: StateFlow<KeyguardState> =
|
||||
finishedKeyguardTransitionStep
|
||||
.map { step -> step.to }
|
||||
.stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN)
|
||||
|
||||
/**
|
||||
* Whether we're currently in a transition to a new [KeyguardState] and haven't yet completed
|
||||
* it.
|
||||
*/
|
||||
val isInTransitionToAnyState =
|
||||
combine(
|
||||
startedKeyguardTransitionStep,
|
||||
finishedKeyguardState,
|
||||
) { startedStep, finishedState ->
|
||||
startedStep.to != finishedState
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of transition into or out of the given [KeyguardState].
|
||||
*
|
||||
@@ -187,4 +207,41 @@ constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
|
||||
return repository.transitions.filter { step -> step.from == fromState }
|
||||
}
|
||||
|
||||
fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
|
||||
return repository.transitions.filter { step -> step.to == toState }
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to start a transition that will ultimately dismiss the keyguard from the current
|
||||
* state.
|
||||
*/
|
||||
fun startDismissKeyguardTransition() {
|
||||
when (startedKeyguardState.value) {
|
||||
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
|
||||
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
|
||||
else ->
|
||||
Log.e(
|
||||
"KeyguardTransitionInteractor",
|
||||
"We don't know how to dismiss keyguard from state " +
|
||||
"${startedKeyguardState.value}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether we're in a transition to the given [KeyguardState], but haven't yet completed it. */
|
||||
fun isInTransitionToState(
|
||||
state: KeyguardState,
|
||||
): Flow<Boolean> {
|
||||
return combine(
|
||||
startedKeyguardTransitionStep,
|
||||
finishedKeyguardState,
|
||||
) { startedStep, finishedState ->
|
||||
startedStep.to == state && finishedState != state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
@SysUISingleton
|
||||
class WindowManagerLockscreenVisibilityInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
|
||||
fromLockscreenInteractor: FromLockscreenTransitionInteractor,
|
||||
fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
|
||||
) {
|
||||
private val defaultSurfaceBehindVisibility =
|
||||
transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
|
||||
|
||||
/**
|
||||
* Surface visibility provided by the From*TransitionInteractor responsible for the currently
|
||||
* RUNNING transition, or null if the current transition does not require special surface
|
||||
* visibility handling.
|
||||
*
|
||||
* An example of transition-specific visibility is swipe to unlock, where the surface should
|
||||
* only be visible after swiping 20% of the way up the screen, and should become invisible again
|
||||
* if the user swipes back down.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val transitionSpecificSurfaceBehindVisibility: Flow<Boolean?> =
|
||||
transitionInteractor.startedKeyguardTransitionStep
|
||||
.flatMapLatest { startedStep ->
|
||||
when (startedStep.from) {
|
||||
KeyguardState.LOCKSCREEN -> {
|
||||
fromLockscreenInteractor.surfaceBehindVisibility
|
||||
}
|
||||
KeyguardState.PRIMARY_BOUNCER -> {
|
||||
fromBouncerInteractor.surfaceBehindVisibility
|
||||
}
|
||||
else -> flowOf(null)
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
/**
|
||||
* Surface visibility, which is either determined by the default visibility in the FINISHED
|
||||
* KeyguardState, or the transition-specific visibility used during certain RUNNING transitions.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val surfaceBehindVisibility: Flow<Boolean> =
|
||||
transitionInteractor
|
||||
.isInTransitionToAnyState
|
||||
.flatMapLatest { isInTransition ->
|
||||
if (!isInTransition) {
|
||||
defaultSurfaceBehindVisibility
|
||||
} else {
|
||||
combine(
|
||||
transitionSpecificSurfaceBehindVisibility,
|
||||
defaultSurfaceBehindVisibility,
|
||||
) { transitionVisibility, defaultVisibility ->
|
||||
// Defer to the transition-specific visibility since we're RUNNING a
|
||||
// transition, but fall back to the default visibility if the current
|
||||
// transition's interactor did not specify a visibility.
|
||||
transitionVisibility ?: defaultVisibility
|
||||
}
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
/**
|
||||
* Whether we're animating, or intend to animate, the surface behind the keyguard via remote
|
||||
* animation. This is used to keep the RemoteAnimationTarget alive until we're done using it.
|
||||
*/
|
||||
val usingKeyguardGoingAwayAnimation: Flow<Boolean> =
|
||||
combine(
|
||||
transitionInteractor.isInTransitionToState(KeyguardState.GONE),
|
||||
transitionInteractor.finishedKeyguardState,
|
||||
surfaceBehindInteractor.isAnimatingSurface
|
||||
) { isInTransitionToGone, finishedState, isAnimatingSurface ->
|
||||
// We may still be animating the surface after the keyguard is fully GONE, since
|
||||
// some animations (like the translation spring) are not tied directly to the
|
||||
// transition step amount.
|
||||
isInTransitionToGone || (finishedState == KeyguardState.GONE && isAnimatingSurface)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
/**
|
||||
* Whether the lockscreen is visible, from the Window Manager (WM) perspective.
|
||||
*
|
||||
* Note: This may briefly be true even if the lockscreen UI has animated out (alpha = 0f), as we
|
||||
* only inform WM once we're done with the keyguard and we're fully GONE. Don't use this if you
|
||||
* want to know if the AOD/clock/notifs/etc. are visible.
|
||||
*/
|
||||
val lockscreenVisibility: Flow<Boolean> =
|
||||
combine(
|
||||
transitionInteractor.startedKeyguardTransitionStep,
|
||||
transitionInteractor.finishedKeyguardState,
|
||||
) { startedStep, finishedState ->
|
||||
// If we finished the transition, use the finished state. If we're running a
|
||||
// transition, use the state we're transitioning FROM. This can be different from
|
||||
// the last finished state if a transition is interrupted. For example, if we were
|
||||
// transitioning from GONE to AOD and then started AOD -> LOCKSCREEN mid-transition,
|
||||
// we want to immediately use the visibility for AOD (lockscreenVisibility=true)
|
||||
// even though the lastFinishedState is still GONE (lockscreenVisibility=false).
|
||||
if (finishedState == startedStep.to) finishedState else startedStep.from
|
||||
}
|
||||
.map(::isLockscreenVisible)
|
||||
.distinctUntilChanged()
|
||||
|
||||
/**
|
||||
* Whether always-on-display (AOD) is visible when the lockscreen is visible, from window
|
||||
* manager's perspective.
|
||||
*
|
||||
* Note: This may be true even if AOD is not user-visible, such as when the light sensor
|
||||
* indicates the device is in the user's pocket. Don't use this if you want to know if the AOD
|
||||
* clock/smartspace/notif icons are visible.
|
||||
*/
|
||||
val aodVisibility: Flow<Boolean> =
|
||||
combine(
|
||||
keyguardInteractor.isDozing,
|
||||
keyguardInteractor.biometricUnlockState,
|
||||
) { isDozing, biometricUnlockState ->
|
||||
// AOD is visible if we're dozing, unless we are wake and unlocking (where we go
|
||||
// directly from AOD to unlocked while dozing).
|
||||
isDozing && !BiometricUnlockModel.isWakeAndUnlock(biometricUnlockState)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
companion object {
|
||||
fun isSurfaceVisible(state: KeyguardState): Boolean {
|
||||
return !isLockscreenVisible(state)
|
||||
}
|
||||
|
||||
fun isLockscreenVisible(state: KeyguardState): Boolean {
|
||||
return state != KeyguardState.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Models the appearance of the surface behind the keyguard, and (optionally) how it should be
|
||||
* animating.
|
||||
*
|
||||
* This is intended to be an atomic, high-level description of the surface's appearance and related
|
||||
* animations, which we can derive from the STARTED/FINISHED transition states rather than the
|
||||
* individual TransitionSteps.
|
||||
*
|
||||
* For example, if we're transitioning from LOCKSCREEN to GONE, that means we should be
|
||||
* animatingFromAlpha 0f -> 1f and animatingFromTranslationY 500f -> 0f.
|
||||
* KeyguardSurfaceBehindAnimator can decide how best to implement this, depending on previously
|
||||
* running animations, spring momentum, and other state.
|
||||
*/
|
||||
data class KeyguardSurfaceBehindModel(
|
||||
val alpha: Float = 1f,
|
||||
|
||||
/**
|
||||
* If provided, animate from this value to [alpha] unless an animation is already running, in
|
||||
* which case we'll animate from the current value to [alpha].
|
||||
*/
|
||||
val animateFromAlpha: Float = alpha,
|
||||
val translationY: Float = 0f,
|
||||
|
||||
/**
|
||||
* If provided, animate from this value to [translationY] unless an animation is already
|
||||
* running, in which case we'll animate from the current value to [translationY].
|
||||
*/
|
||||
val animateFromTranslationY: Float = translationY,
|
||||
) {
|
||||
fun willAnimateAlpha(): Boolean {
|
||||
return animateFromAlpha != alpha
|
||||
}
|
||||
|
||||
fun willAnimateTranslationY(): Boolean {
|
||||
return animateFromTranslationY != translationY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.binder
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.Matrix
|
||||
import android.util.Log
|
||||
import android.view.RemoteAnimationTarget
|
||||
import android.view.SurfaceControl
|
||||
import android.view.SyncRtSurfaceTransactionApplier
|
||||
import android.view.View
|
||||
import androidx.dynamicanimation.animation.FloatValueHolder
|
||||
import androidx.dynamicanimation.animation.SpringAnimation
|
||||
import androidx.dynamicanimation.animation.SpringForce
|
||||
import com.android.keyguard.KeyguardViewController
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.keyguard.TAG
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
|
||||
import com.android.wm.shell.animation.Interpolators
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Applies [KeyguardSurfaceBehindViewParams] to a RemoteAnimationTarget, starting and managing
|
||||
* animations as needed.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class KeyguardSurfaceBehindParamsApplier
|
||||
@Inject
|
||||
constructor(
|
||||
@Main private val executor: Executor,
|
||||
private val keyguardViewController: KeyguardViewController,
|
||||
private val interactor: KeyguardSurfaceBehindInteractor,
|
||||
) {
|
||||
private var surfaceBehind: RemoteAnimationTarget? = null
|
||||
private val surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
|
||||
get() = SyncRtSurfaceTransactionApplier(keyguardViewController.viewRootImpl.view)
|
||||
|
||||
private val matrix = Matrix()
|
||||
private val tmpFloat = FloatArray(9)
|
||||
|
||||
private var animatedTranslationY = FloatValueHolder()
|
||||
private val translateYSpring =
|
||||
SpringAnimation(animatedTranslationY).apply {
|
||||
spring =
|
||||
SpringForce().apply {
|
||||
stiffness = 200f
|
||||
dampingRatio = 1f
|
||||
}
|
||||
addUpdateListener { _, _, _ -> applyToSurfaceBehind() }
|
||||
addEndListener { _, _, _, _ ->
|
||||
try {
|
||||
updateIsAnimatingSurface()
|
||||
} catch (e: NullPointerException) {
|
||||
// TODO(b/291645410): Remove when we can isolate DynamicAnimations.
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var animatedAlpha = 0f
|
||||
private var alphaAnimator =
|
||||
ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
duration = 500
|
||||
interpolator = Interpolators.ALPHA_IN
|
||||
addUpdateListener {
|
||||
animatedAlpha = it.animatedValue as Float
|
||||
applyToSurfaceBehind()
|
||||
}
|
||||
addListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
updateIsAnimatingSurface()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewParams to apply to the surface provided to [applyParamsToSurface]. If the surface is null
|
||||
* these will be applied once someone gives us a surface via [applyParamsToSurface].
|
||||
*/
|
||||
var viewParams: KeyguardSurfaceBehindModel = KeyguardSurfaceBehindModel()
|
||||
set(newParams) {
|
||||
field = newParams
|
||||
startOrUpdateAnimators()
|
||||
applyToSurfaceBehind()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides us with a surface to animate. We'll apply the [viewParams] to this surface and start
|
||||
* any necessary animations.
|
||||
*/
|
||||
fun applyParamsToSurface(surface: RemoteAnimationTarget) {
|
||||
this.surfaceBehind = surface
|
||||
startOrUpdateAnimators()
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies us that the [RemoteAnimationTarget] has been released, one way or another.
|
||||
* Attempting to animate a released target will cause a crash.
|
||||
*
|
||||
* This can be called either because we finished animating the surface naturally, or by WM
|
||||
* because external factors cancelled the remote animation (timeout, re-lock, etc). If it's the
|
||||
* latter, cancel any outstanding animations we have.
|
||||
*/
|
||||
fun notifySurfaceReleased() {
|
||||
surfaceBehind = null
|
||||
|
||||
if (alphaAnimator.isRunning) {
|
||||
alphaAnimator.cancel()
|
||||
}
|
||||
|
||||
if (translateYSpring.isRunning) {
|
||||
translateYSpring.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startOrUpdateAnimators() {
|
||||
if (surfaceBehind == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (viewParams.willAnimateAlpha()) {
|
||||
var fromAlpha = viewParams.animateFromAlpha
|
||||
|
||||
if (alphaAnimator.isRunning) {
|
||||
alphaAnimator.cancel()
|
||||
fromAlpha = animatedAlpha
|
||||
}
|
||||
|
||||
alphaAnimator.setFloatValues(fromAlpha, viewParams.alpha)
|
||||
alphaAnimator.start()
|
||||
}
|
||||
|
||||
if (viewParams.willAnimateTranslationY()) {
|
||||
if (!translateYSpring.isRunning) {
|
||||
// If the spring isn't running yet, set the start value. Otherwise, respect the
|
||||
// current position.
|
||||
animatedTranslationY.value = viewParams.animateFromTranslationY
|
||||
}
|
||||
|
||||
translateYSpring.animateToFinalPosition(viewParams.translationY)
|
||||
}
|
||||
|
||||
updateIsAnimatingSurface()
|
||||
}
|
||||
|
||||
private fun updateIsAnimatingSurface() {
|
||||
interactor.setAnimatingSurface(translateYSpring.isRunning || alphaAnimator.isRunning)
|
||||
}
|
||||
|
||||
private fun applyToSurfaceBehind() {
|
||||
surfaceBehind?.leash?.let { sc ->
|
||||
executor.execute {
|
||||
if (surfaceBehind == null) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Attempting to modify params of surface that isn't " +
|
||||
"animating. Ignoring."
|
||||
)
|
||||
matrix.set(Matrix.IDENTITY_MATRIX)
|
||||
return@execute
|
||||
}
|
||||
|
||||
val translationY =
|
||||
if (translateYSpring.isRunning) animatedTranslationY.value
|
||||
else viewParams.translationY
|
||||
|
||||
val alpha =
|
||||
if (alphaAnimator.isRunning) {
|
||||
animatedAlpha
|
||||
} else {
|
||||
viewParams.alpha
|
||||
}
|
||||
|
||||
if (
|
||||
keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
|
||||
sc.isValid
|
||||
) {
|
||||
with(SurfaceControl.Transaction()) {
|
||||
setMatrix(
|
||||
sc,
|
||||
matrix.apply { setTranslate(/* dx= */ 0f, translationY) },
|
||||
tmpFloat
|
||||
)
|
||||
setAlpha(sc, alpha)
|
||||
apply()
|
||||
}
|
||||
} else {
|
||||
surfaceTransactionApplier.scheduleApply(
|
||||
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(sc)
|
||||
.withMatrix(matrix.apply { setTranslate(/* dx= */ 0f, translationY) })
|
||||
.withAlpha(alpha)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.binder
|
||||
|
||||
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Binds the [WindowManagerLockscreenVisibilityManager] "view", which manages the visibility of the
|
||||
* surface behind the keyguard.
|
||||
*/
|
||||
object KeyguardSurfaceBehindViewBinder {
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
viewModel: KeyguardSurfaceBehindViewModel,
|
||||
applier: KeyguardSurfaceBehindParamsApplier,
|
||||
scope: CoroutineScope
|
||||
) {
|
||||
scope.launch { viewModel.surfaceBehindViewParams.collect { applier.viewParams = it } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.binder
|
||||
|
||||
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
|
||||
import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Binds the [WindowManagerLockscreenVisibilityManager] "view", which manages the visibility of the
|
||||
* surface behind the keyguard.
|
||||
*/
|
||||
object WindowManagerLockscreenVisibilityViewBinder {
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
viewModel: WindowManagerLockscreenVisibilityViewModel,
|
||||
lockscreenVisibilityManager: WindowManagerLockscreenVisibilityManager,
|
||||
scope: CoroutineScope
|
||||
) {
|
||||
scope.launch {
|
||||
viewModel.surfaceBehindVisibility.collect {
|
||||
lockscreenVisibilityManager.setSurfaceBehindVisibility(it)
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
viewModel.lockscreenVisibility.collect {
|
||||
lockscreenVisibilityManager.setLockscreenShown(it)
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
viewModel.aodVisibility.collect { lockscreenVisibilityManager.setAodVisible(it) }
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
viewModel.surfaceBehindAnimating.collect {
|
||||
lockscreenVisibilityManager.setUsingGoingAwayRemoteAnimation(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.ui.viewmodel
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
|
||||
import javax.inject.Inject
|
||||
|
||||
@SysUISingleton
|
||||
class KeyguardSurfaceBehindViewModel
|
||||
@Inject
|
||||
constructor(interactor: KeyguardSurfaceBehindInteractor) {
|
||||
val surfaceBehindViewParams = interactor.viewParams
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
|
||||
import javax.inject.Inject
|
||||
|
||||
@SysUISingleton
|
||||
class WindowManagerLockscreenVisibilityViewModel
|
||||
@Inject
|
||||
constructor(interactor: WindowManagerLockscreenVisibilityInteractor) {
|
||||
val surfaceBehindVisibility = interactor.surfaceBehindVisibility
|
||||
val surfaceBehindAnimating = interactor.usingKeyguardGoingAwayAnimation
|
||||
val lockscreenVisibility = interactor.lockscreenVisibility
|
||||
val aodVisibility = interactor.aodVisibility
|
||||
}
|
||||
@@ -634,7 +634,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
|
||||
|
||||
private final ActivityIntentHelper mActivityIntentHelper;
|
||||
|
||||
private final NotificationStackScrollLayoutController mStackScrollerController;
|
||||
public final NotificationStackScrollLayoutController mStackScrollerController;
|
||||
|
||||
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
|
||||
(extractor, which) -> updateTheme();
|
||||
|
||||
@@ -22,6 +22,8 @@ import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConst
|
||||
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
|
||||
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
|
||||
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
|
||||
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
|
||||
import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
@@ -60,10 +62,14 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInte
|
||||
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
|
||||
import com.android.systemui.bouncer.ui.BouncerView;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dreams.DreamOverlayStateController;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
|
||||
import com.android.systemui.navigationbar.NavigationBarView;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.navigationbar.TaskbarDelegate;
|
||||
@@ -86,8 +92,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.unfold.FoldAodAnimationController;
|
||||
import com.android.systemui.unfold.SysUIUnfoldComponent;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -97,6 +101,9 @@ import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.Lazy;
|
||||
import kotlinx.coroutines.CoroutineDispatcher;
|
||||
|
||||
/**
|
||||
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
|
||||
* via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
|
||||
@@ -281,6 +288,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
private int mLastBiometricMode;
|
||||
private boolean mLastScreenOffAnimationPlaying;
|
||||
private float mQsExpansion;
|
||||
|
||||
private FeatureFlags mFlags;
|
||||
|
||||
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
|
||||
private boolean mIsBackAnimationEnabled;
|
||||
private final boolean mUdfpsNewTouchDetectionEnabled;
|
||||
@@ -326,6 +336,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
}
|
||||
};
|
||||
private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor;
|
||||
|
||||
@Inject
|
||||
public StatusBarKeyguardViewManager(
|
||||
@@ -352,7 +363,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
BouncerView primaryBouncerView,
|
||||
AlternateBouncerInteractor alternateBouncerInteractor,
|
||||
UdfpsOverlayInteractor udfpsOverlayInteractor,
|
||||
ActivityStarter activityStarter
|
||||
ActivityStarter activityStarter,
|
||||
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
||||
@Main CoroutineDispatcher mainDispatcher,
|
||||
Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor
|
||||
) {
|
||||
mContext = context;
|
||||
mViewMediatorCallback = callback;
|
||||
@@ -370,6 +384,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mShadeController = shadeController;
|
||||
mLatencyTracker = latencyTracker;
|
||||
mKeyguardSecurityModel = keyguardSecurityModel;
|
||||
mFlags = featureFlags;
|
||||
mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
|
||||
mPrimaryBouncerInteractor = primaryBouncerInteractor;
|
||||
mPrimaryBouncerView = primaryBouncerView;
|
||||
@@ -381,8 +396,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mUdfpsNewTouchDetectionEnabled = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION);
|
||||
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
|
||||
mActivityStarter = activityStarter;
|
||||
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
|
||||
mMainDispatcher = mainDispatcher;
|
||||
mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor;
|
||||
}
|
||||
|
||||
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||
CoroutineDispatcher mMainDispatcher;
|
||||
|
||||
@Override
|
||||
public void registerCentralSurfaces(CentralSurfaces centralSurfaces,
|
||||
ShadeViewController shadeViewController,
|
||||
@@ -429,6 +450,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
}
|
||||
}
|
||||
|
||||
private KeyguardStateController.Callback mKeyguardStateControllerCallback =
|
||||
new KeyguardStateController.Callback() {
|
||||
@Override
|
||||
public void onUnlockedChanged() {
|
||||
updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
|
||||
}
|
||||
};
|
||||
|
||||
private void registerListeners() {
|
||||
mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
|
||||
mStatusBarStateController.addCallback(this);
|
||||
@@ -442,6 +471,32 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mDockManager.addListener(mDockEventListener);
|
||||
mIsDocked = mDockManager.isDocked();
|
||||
}
|
||||
mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
|
||||
|
||||
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
mShadeViewController.postToView(() ->
|
||||
collectFlow(
|
||||
getViewRootImpl().getView(),
|
||||
combineFlows(
|
||||
mKeyguardTransitionInteractor.getFinishedKeyguardState(),
|
||||
mWmLockscreenVisibilityInteractor.get()
|
||||
.getUsingKeyguardGoingAwayAnimation(),
|
||||
(finishedState, animating) ->
|
||||
KeyguardInteractor.Companion.isKeyguardVisibleInState(
|
||||
finishedState)
|
||||
|| animating),
|
||||
this::consumeShowStatusBarKeyguardView));
|
||||
}
|
||||
}
|
||||
|
||||
private void consumeShowStatusBarKeyguardView(boolean show) {
|
||||
if (show != mLastShowing) {
|
||||
if (show) {
|
||||
show(null);
|
||||
} else {
|
||||
hide(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Register a callback, to be invoked by the Predictive Back system. */
|
||||
@@ -1313,6 +1368,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
hideAlternateBouncer(false);
|
||||
executeAfterKeyguardGoneAction();
|
||||
}
|
||||
|
||||
if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
|
||||
mKeyguardTransitionInteractor.startDismissKeyguardTransition();
|
||||
}
|
||||
}
|
||||
|
||||
/** Display security message to relevant KeyguardMessageArea. */
|
||||
|
||||
@@ -29,7 +29,7 @@ import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** A class allowing Java classes to collect on Kotlin flows. */
|
||||
@@ -75,3 +75,7 @@ fun <T> collectFlow(
|
||||
repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
|
||||
return combine(flow1, flow2, bifunction)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ import com.android.systemui.classifier.FalsingA11yDelegate
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
|
||||
import com.android.systemui.log.SessionTracker
|
||||
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
@@ -145,6 +147,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
private lateinit var testableResources: TestableResources
|
||||
private lateinit var sceneTestUtils: SceneTestUtils
|
||||
private lateinit var sceneInteractor: SceneInteractor
|
||||
private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
|
||||
private lateinit var authenticationInteractor: AuthenticationInteractor
|
||||
private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState>
|
||||
|
||||
@@ -184,6 +187,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
|
||||
featureFlags.set(Flags.SCENE_CONTAINER, false)
|
||||
featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false)
|
||||
featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
|
||||
|
||||
keyguardPasswordViewController =
|
||||
KeyguardPasswordViewController(
|
||||
@@ -206,6 +210,9 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
whenever(userInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID)
|
||||
sceneTestUtils = SceneTestUtils(this)
|
||||
sceneInteractor = sceneTestUtils.sceneInteractor()
|
||||
keyguardTransitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(sceneTestUtils.testScope.backgroundScope)
|
||||
.keyguardTransitionInteractor
|
||||
sceneTransitionStateFlow =
|
||||
MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen))
|
||||
sceneInteractor.setTransitionState(sceneTransitionStateFlow)
|
||||
@@ -243,9 +250,9 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
{ JavaAdapter(sceneTestUtils.testScope.backgroundScope) },
|
||||
userInteractor,
|
||||
faceAuthAccessibilityDelegate,
|
||||
) {
|
||||
authenticationInteractor
|
||||
}
|
||||
keyguardTransitionInteractor,
|
||||
{ authenticationInteractor },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -49,7 +49,7 @@ import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FakeFeatureFlags;
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
@@ -164,8 +164,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
|
||||
mVibrator,
|
||||
mAuthRippleController,
|
||||
mResources,
|
||||
new KeyguardTransitionInteractor(mTransitionRepository,
|
||||
TestScopeProvider.getTestScope().getBackgroundScope()),
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
TestScopeProvider.getTestScope().getBackgroundScope(),
|
||||
mTransitionRepository).getKeyguardTransitionInteractor(),
|
||||
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
|
||||
mFeatureFlags,
|
||||
mPrimaryBouncerInteractor
|
||||
|
||||
@@ -224,7 +224,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
||||
mScreenOffAnimationController, mAuthController, mShadeExpansionStateManager,
|
||||
mShadeWindowLogger);
|
||||
mFeatureFlags = new FakeFeatureFlags();
|
||||
|
||||
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
|
||||
|
||||
DejankUtils.setImmediate(true);
|
||||
|
||||
@@ -957,7 +957,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
||||
mSystemClock,
|
||||
mDispatcher,
|
||||
() -> mDreamingToLockscreenTransitionViewModel,
|
||||
mSystemPropertiesHelper);
|
||||
mSystemPropertiesHelper,
|
||||
() -> mock(WindowManagerLockscreenVisibilityManager.class));
|
||||
mViewMediator.start();
|
||||
|
||||
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.dump.logcatLogBuffer
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
|
||||
import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
|
||||
@@ -168,7 +169,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
|
||||
biometricSettingsRepository = FakeBiometricSettingsRepository()
|
||||
deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
|
||||
trustRepository = FakeTrustRepository()
|
||||
featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
set(FACE_AUTH_REFACTOR, true)
|
||||
set(KEYGUARD_WM_STATE_REFACTOR, false)
|
||||
}
|
||||
val withDeps =
|
||||
KeyguardInteractorFactory.create(
|
||||
featureFlags = featureFlags,
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.atLeastOnce
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@@ -193,7 +194,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
||||
assertThat(underTest.isKeyguardShowing()).isFalse()
|
||||
|
||||
val captor = argumentCaptor<KeyguardStateController.Callback>()
|
||||
verify(keyguardStateController).addCallback(captor.capture())
|
||||
verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
|
||||
|
||||
whenever(keyguardStateController.isShowing).thenReturn(true)
|
||||
captor.value.onKeyguardShowingChanged()
|
||||
@@ -255,7 +256,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
val captor = argumentCaptor<KeyguardStateController.Callback>()
|
||||
verify(keyguardStateController).addCallback(captor.capture())
|
||||
verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
|
||||
|
||||
whenever(keyguardStateController.isOccluded).thenReturn(true)
|
||||
captor.value.onKeyguardShowingChanged()
|
||||
@@ -280,7 +281,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
||||
assertThat(isKeyguardUnlocked).isFalse()
|
||||
|
||||
val captor = argumentCaptor<KeyguardStateController.Callback>()
|
||||
verify(keyguardStateController).addCallback(captor.capture())
|
||||
verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
|
||||
|
||||
whenever(keyguardStateController.isUnlocked).thenReturn(true)
|
||||
captor.value.onUnlockedChanged()
|
||||
@@ -454,7 +455,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
val captor = argumentCaptor<KeyguardStateController.Callback>()
|
||||
verify(keyguardStateController).addCallback(captor.capture())
|
||||
verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
|
||||
|
||||
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
|
||||
captor.value.onKeyguardGoingAwayChanged()
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.data.repository
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectValues
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class KeyguardSurfaceBehindRepositoryImplTest : SysuiTestCase() {
|
||||
private val testScope = TestScope()
|
||||
|
||||
private lateinit var underTest: KeyguardSurfaceBehindRepositoryImpl
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
underTest = KeyguardSurfaceBehindRepositoryImpl()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetAnimatingSurface() {
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.isAnimatingSurface)
|
||||
|
||||
runCurrent()
|
||||
underTest.setAnimatingSurface(true)
|
||||
runCurrent()
|
||||
underTest.setAnimatingSurface(false)
|
||||
runCurrent()
|
||||
|
||||
// Default (first) value should be false.
|
||||
assertThat(values).isEqualTo(listOf(false, true, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.coroutines.collectValues
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
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.shade.data.repository.FakeShadeRepository
|
||||
import dagger.Lazy
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertTrue
|
||||
import junit.framework.Assert.fail
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FromLockscreenTransitionInteractorTest : KeyguardTransitionInteractorTestCase() {
|
||||
private lateinit var underTest: FromLockscreenTransitionInteractor
|
||||
|
||||
// Override the fromLockscreenTransitionInteractor provider from the superclass so our underTest
|
||||
// interactor is provided to any classes that need it.
|
||||
override var fromLockscreenTransitionInteractorLazy: Lazy<FromLockscreenTransitionInteractor>? =
|
||||
Lazy {
|
||||
underTest
|
||||
}
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
underTest =
|
||||
FromLockscreenTransitionInteractor(
|
||||
transitionRepository = super.transitionRepository,
|
||||
transitionInteractor = super.transitionInteractor,
|
||||
scope = super.testScope.backgroundScope,
|
||||
keyguardInteractor = super.keyguardInteractor,
|
||||
flags = FakeFeatureFlags(),
|
||||
shadeRepository = FakeShadeRepository(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSurfaceBehindVisibility_nonNullOnlyForRelevantTransitions() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.surfaceBehindVisibility)
|
||||
runCurrent()
|
||||
|
||||
// Transition-specific surface visibility should be null ("don't care") initially.
|
||||
assertEquals(
|
||||
listOf(
|
||||
null,
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.AOD,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null, // LOCKSCREEN -> AOD does not have any specific surface visibility.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null,
|
||||
true, // Surface is made visible immediately during LOCKSCREEN -> GONE
|
||||
),
|
||||
values
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSurfaceBehindModel() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.surfaceBehindModel)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
values,
|
||||
listOf(
|
||||
null, // We should start null ("don't care").
|
||||
)
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.AOD,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null, // LOCKSCREEN -> AOD does not have specific view params.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.RUNNING,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
value = 0.01f,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.RUNNING,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
value = 0.99f,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(3, values.size)
|
||||
val model1percent = values[1]
|
||||
val model99percent = values[2]
|
||||
|
||||
try {
|
||||
// We should initially have an alpha of 0f when unlocking, so the surface is not
|
||||
// visible
|
||||
// while lockscreen UI animates out.
|
||||
assertEquals(0f, model1percent!!.alpha)
|
||||
|
||||
// By the end it should probably be visible.
|
||||
assertTrue(model99percent!!.alpha > 0f)
|
||||
} catch (e: NullPointerException) {
|
||||
fail("surfaceBehindModel was unexpectedly null.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.coroutines.collectValues
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
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.util.mockito.mock
|
||||
import dagger.Lazy
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertTrue
|
||||
import junit.framework.Assert.fail
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorTestCase() {
|
||||
private lateinit var underTest: FromPrimaryBouncerTransitionInteractor
|
||||
|
||||
// Override the fromPrimaryBouncerTransitionInteractor provider from the superclass so our
|
||||
// underTest interactor is provided to any classes that need it.
|
||||
override var fromPrimaryBouncerTransitionInteractorLazy:
|
||||
Lazy<FromPrimaryBouncerTransitionInteractor>? =
|
||||
Lazy {
|
||||
underTest
|
||||
}
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
underTest =
|
||||
FromPrimaryBouncerTransitionInteractor(
|
||||
transitionRepository = super.transitionRepository,
|
||||
transitionInteractor = super.transitionInteractor,
|
||||
scope = super.testScope.backgroundScope,
|
||||
keyguardInteractor = super.keyguardInteractor,
|
||||
flags = FakeFeatureFlags(),
|
||||
keyguardSecurityModel = mock(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSurfaceBehindVisibility() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.surfaceBehindVisibility)
|
||||
runCurrent()
|
||||
|
||||
// Transition-specific surface visibility should be null ("don't care") initially.
|
||||
assertEquals(
|
||||
listOf(
|
||||
null,
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null, // PRIMARY_BOUNCER -> LOCKSCREEN does not have any specific visibility.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.RUNNING,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
value = 0.01f,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null,
|
||||
false, // Surface is only made visible once the bouncer UI animates out.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
value = 0.99f,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null,
|
||||
false,
|
||||
true, // Surface should eventually be visible.
|
||||
),
|
||||
values
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSurfaceBehindModel() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.surfaceBehindModel)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
null, // PRIMARY_BOUNCER -> LOCKSCREEN does not have specific view params.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.RUNNING,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
value = 0.01f,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.RUNNING,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
value = 0.99f,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(3, values.size)
|
||||
val model1percent = values[1]
|
||||
val model99percent = values[2]
|
||||
|
||||
try {
|
||||
// We should initially have an alpha of 0f when unlocking, so the surface is not
|
||||
// visible
|
||||
// while lockscreen UI animates out.
|
||||
assertEquals(0f, model1percent!!.alpha)
|
||||
|
||||
// By the end it should probably be visible.
|
||||
assertTrue(model99percent!!.alpha > 0f)
|
||||
} catch (e: NullPointerException) {
|
||||
fail("surfaceBehindModel was unexpectedly null.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectValues
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertTrue
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
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.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations.initMocks
|
||||
|
||||
@SmallTest
|
||||
@RoboPilotTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var underTest: KeyguardSurfaceBehindInteractor
|
||||
private lateinit var repository: FakeKeyguardSurfaceBehindRepository
|
||||
|
||||
@Mock
|
||||
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
|
||||
@Mock
|
||||
private lateinit var fromPrimaryBouncerTransitionInteractor:
|
||||
FromPrimaryBouncerTransitionInteractor
|
||||
|
||||
private val lockscreenSurfaceBehindModel = KeyguardSurfaceBehindModel(alpha = 0.33f)
|
||||
private val primaryBouncerSurfaceBehindModel = KeyguardSurfaceBehindModel(alpha = 0.66f)
|
||||
|
||||
private val testScope = TestScope()
|
||||
|
||||
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
|
||||
private lateinit var transitionInteractor: KeyguardTransitionInteractor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
initMocks(this)
|
||||
|
||||
whenever(fromLockscreenTransitionInteractor.surfaceBehindModel)
|
||||
.thenReturn(flowOf(lockscreenSurfaceBehindModel))
|
||||
whenever(fromPrimaryBouncerTransitionInteractor.surfaceBehindModel)
|
||||
.thenReturn(flowOf(primaryBouncerSurfaceBehindModel))
|
||||
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
|
||||
transitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
scope = testScope.backgroundScope,
|
||||
repository = transitionRepository,
|
||||
)
|
||||
.keyguardTransitionInteractor
|
||||
|
||||
repository = FakeKeyguardSurfaceBehindRepository()
|
||||
underTest =
|
||||
KeyguardSurfaceBehindInteractor(
|
||||
repository = repository,
|
||||
fromLockscreenInteractor = fromLockscreenTransitionInteractor,
|
||||
fromPrimaryBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
|
||||
transitionInteractor = transitionInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun viewParamsSwitchToCorrectFlow() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.viewParams)
|
||||
|
||||
// Start on the LOCKSCREEN.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// We're on LOCKSCREEN; we should be using the default params.
|
||||
assertEquals(1, values.size)
|
||||
assertTrue(values[0].alpha == 0f)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// We're going from LOCKSCREEN -> GONE, we should be using the lockscreen interactor's
|
||||
// surface behind model.
|
||||
assertEquals(2, values.size)
|
||||
assertEquals(values[1], lockscreenSurfaceBehindModel)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// We're going from PRIMARY_BOUNCER -> GONE, we should be using the bouncer interactor's
|
||||
// surface behind model.
|
||||
assertEquals(3, values.size)
|
||||
assertEquals(values[2], primaryBouncerSurfaceBehindModel)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// Once PRIMARY_BOUNCER -> GONE finishes, we should be using default params, which is
|
||||
// alpha=1f when we're GONE.
|
||||
assertEquals(4, values.size)
|
||||
assertEquals(1f, values[3].alpha)
|
||||
}
|
||||
}
|
||||
@@ -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.domain.interactor
|
||||
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
||||
open class KeyguardTransitionInteractorTestCase : SysuiTestCase() {
|
||||
val testDispatcher = StandardTestDispatcher()
|
||||
val testScope = TestScope(testDispatcher)
|
||||
|
||||
lateinit var keyguardRepository: FakeKeyguardRepository
|
||||
lateinit var transitionRepository: FakeKeyguardTransitionRepository
|
||||
|
||||
lateinit var keyguardInteractor: KeyguardInteractor
|
||||
lateinit var transitionInteractor: KeyguardTransitionInteractor
|
||||
|
||||
/**
|
||||
* Replace these lazy providers with non-null ones if you want test dependencies to use a real
|
||||
* instance of the interactor for the test.
|
||||
*/
|
||||
open var fromLockscreenTransitionInteractorLazy: Lazy<FromLockscreenTransitionInteractor>? =
|
||||
null
|
||||
open var fromPrimaryBouncerTransitionInteractorLazy:
|
||||
Lazy<FromPrimaryBouncerTransitionInteractor>? =
|
||||
null
|
||||
|
||||
open fun setUp() {
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
|
||||
keyguardInteractor =
|
||||
KeyguardInteractorFactory.create(repository = keyguardRepository).keyguardInteractor
|
||||
|
||||
transitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
repository = transitionRepository,
|
||||
keyguardInteractor = keyguardInteractor,
|
||||
scope = testScope.backgroundScope,
|
||||
fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractorLazy
|
||||
?: Lazy { mock() },
|
||||
fromPrimaryBouncerTransitionInteractor =
|
||||
fromPrimaryBouncerTransitionInteractorLazy ?: Lazy { mock() },
|
||||
)
|
||||
.also {
|
||||
fromLockscreenTransitionInteractorLazy = it.fromLockscreenTransitionInteractor
|
||||
fromPrimaryBouncerTransitionInteractorLazy =
|
||||
it.fromPrimaryBouncerTransitionInteractor
|
||||
}
|
||||
.keyguardTransitionInteractor
|
||||
}
|
||||
}
|
||||
@@ -104,12 +104,21 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
||||
|
||||
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
|
||||
|
||||
featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
set(Flags.FACE_AUTH_REFACTOR, true)
|
||||
set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
|
||||
}
|
||||
|
||||
transitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
scope = testScope,
|
||||
repository = transitionRepository,
|
||||
keyguardInteractor = createKeyguardInteractor(),
|
||||
fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
|
||||
fromPrimaryBouncerTransitionInteractor = {
|
||||
fromPrimaryBouncerTransitionInteractor
|
||||
},
|
||||
)
|
||||
.keyguardTransitionInteractor
|
||||
|
||||
@@ -119,6 +128,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
||||
keyguardInteractor = createKeyguardInteractor(),
|
||||
transitionRepository = transitionRepository,
|
||||
transitionInteractor = transitionInteractor,
|
||||
flags = featureFlags,
|
||||
shadeRepository = shadeRepository,
|
||||
)
|
||||
.apply { start() }
|
||||
@@ -129,6 +139,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
|
||||
keyguardInteractor = createKeyguardInteractor(),
|
||||
transitionRepository = transitionRepository,
|
||||
transitionInteractor = transitionInteractor,
|
||||
flags = featureFlags,
|
||||
keyguardSecurityModel = keyguardSecurityModel,
|
||||
)
|
||||
.apply { start() }
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectValues
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import junit.framework.Assert.assertEquals
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
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.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations.initMocks
|
||||
|
||||
@SmallTest
|
||||
@RoboPilotTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var underTest: WindowManagerLockscreenVisibilityInteractor
|
||||
|
||||
@Mock private lateinit var surfaceBehindInteractor: KeyguardSurfaceBehindInteractor
|
||||
@Mock
|
||||
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
|
||||
@Mock
|
||||
private lateinit var fromPrimaryBouncerTransitionInteractor:
|
||||
FromPrimaryBouncerTransitionInteractor
|
||||
|
||||
private val lockscreenSurfaceVisibilityFlow = MutableStateFlow<Boolean?>(false)
|
||||
private val primaryBouncerSurfaceVisibilityFlow = MutableStateFlow<Boolean?>(false)
|
||||
private val surfaceBehindIsAnimatingFlow = MutableStateFlow(false)
|
||||
|
||||
private val testScope = TestScope()
|
||||
|
||||
private lateinit var keyguardInteractor: KeyguardInteractor
|
||||
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
|
||||
private lateinit var transitionInteractor: KeyguardTransitionInteractor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
initMocks(this)
|
||||
|
||||
whenever(fromLockscreenTransitionInteractor.surfaceBehindVisibility)
|
||||
.thenReturn(lockscreenSurfaceVisibilityFlow)
|
||||
whenever(fromPrimaryBouncerTransitionInteractor.surfaceBehindVisibility)
|
||||
.thenReturn(primaryBouncerSurfaceVisibilityFlow)
|
||||
whenever(surfaceBehindInteractor.isAnimatingSurface)
|
||||
.thenReturn(surfaceBehindIsAnimatingFlow)
|
||||
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
|
||||
transitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
scope = testScope.backgroundScope,
|
||||
repository = transitionRepository,
|
||||
)
|
||||
.also { keyguardInteractor = it.keyguardInteractor }
|
||||
.keyguardTransitionInteractor
|
||||
|
||||
underTest =
|
||||
WindowManagerLockscreenVisibilityInteractor(
|
||||
keyguardInteractor = keyguardInteractor,
|
||||
transitionInteractor = transitionInteractor,
|
||||
surfaceBehindInteractor = surfaceBehindInteractor,
|
||||
fromLockscreenTransitionInteractor,
|
||||
fromPrimaryBouncerTransitionInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun surfaceBehindVisibility_switchesToCorrectFlow() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.surfaceBehindVisibility)
|
||||
|
||||
// Start on LOCKSCREEN.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false, // We should start with the surface invisible on LOCKSCREEN.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
val lockscreenSpecificSurfaceVisibility = true
|
||||
lockscreenSurfaceVisibilityFlow.emit(lockscreenSpecificSurfaceVisibility)
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// We started a transition from LOCKSCREEN, we should be using the value emitted by the
|
||||
// lockscreenSurfaceVisibilityFlow.
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
lockscreenSpecificSurfaceVisibility,
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
// Go back to LOCKSCREEN, since we won't emit 'true' twice in a row.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.GONE,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.GONE,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
lockscreenSpecificSurfaceVisibility,
|
||||
false, // FINISHED (LOCKSCREEN)
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
val bouncerSpecificVisibility = true
|
||||
primaryBouncerSurfaceVisibilityFlow.emit(bouncerSpecificVisibility)
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.PRIMARY_BOUNCER,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// We started a transition from PRIMARY_BOUNCER, we should be using the value emitted by
|
||||
// the
|
||||
// primaryBouncerSurfaceVisibilityFlow.
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
lockscreenSpecificSurfaceVisibility,
|
||||
false,
|
||||
bouncerSpecificVisibility,
|
||||
),
|
||||
values
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUsingGoingAwayAnimation_duringTransitionToGone() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
|
||||
|
||||
// Start on LOCKSCREEN.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false, // Not using the animation when we're just sitting on LOCKSCREEN.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
surfaceBehindIsAnimatingFlow.emit(true)
|
||||
runCurrent()
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
true, // Still true when we're FINISHED -> GONE, since we're still animating.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
surfaceBehindIsAnimatingFlow.emit(false)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
true,
|
||||
false, // False once the animation ends.
|
||||
),
|
||||
values
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGone() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
|
||||
|
||||
// Start on LOCKSCREEN.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false, // Not using the animation when we're just sitting on LOCKSCREEN.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
surfaceBehindIsAnimatingFlow.emit(true)
|
||||
runCurrent()
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
true, // We're happily animating while transitioning to gone.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
// Oh no, we're still surfaceBehindAnimating=true, but no longer transitioning to GONE.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.AOD,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
true,
|
||||
false, // Despite the animator still running, this should be false.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
surfaceBehindIsAnimatingFlow.emit(false)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
false,
|
||||
true,
|
||||
false, // The animator ending should have no effect.
|
||||
),
|
||||
values
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lockscreenVisibility_visibleWhenGone() =
|
||||
testScope.runTest {
|
||||
val values by collectValues(underTest.lockscreenVisibility)
|
||||
|
||||
// Start on LOCKSCREEN.
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.AOD,
|
||||
to = KeyguardState.LOCKSCREEN,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
true, // Unsurprisingly, we should start with the lockscreen visible on
|
||||
// LOCKSCREEN.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.STARTED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
true, // Lockscreen remains visible while we're transitioning to GONE.
|
||||
),
|
||||
values
|
||||
)
|
||||
|
||||
transitionRepository.sendTransitionStep(
|
||||
TransitionStep(
|
||||
transitionState = TransitionState.FINISHED,
|
||||
from = KeyguardState.LOCKSCREEN,
|
||||
to = KeyguardState.GONE,
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
true,
|
||||
false, // Once we're fully GONE, the lockscreen should not be visible.
|
||||
),
|
||||
values
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.binder
|
||||
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.RemoteAnimationTarget
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardViewController
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.animation.AnimatorTestRule
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertNull
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RoboPilotTest
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
class KeyguardSurfaceBehindParamsApplierTest : SysuiTestCase() {
|
||||
@get:Rule val animatorTestRule = AnimatorTestRule()
|
||||
|
||||
private lateinit var underTest: KeyguardSurfaceBehindParamsApplier
|
||||
private lateinit var executor: FakeExecutor
|
||||
|
||||
@Mock private lateinit var keyguardViewController: KeyguardViewController
|
||||
|
||||
@Mock private lateinit var interactor: KeyguardSurfaceBehindInteractor
|
||||
|
||||
@Mock private lateinit var remoteAnimationTarget: RemoteAnimationTarget
|
||||
|
||||
private var isAnimatingSurface: Boolean? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
executor = FakeExecutor(FakeSystemClock())
|
||||
underTest =
|
||||
KeyguardSurfaceBehindParamsApplier(
|
||||
executor = executor,
|
||||
keyguardViewController = keyguardViewController,
|
||||
interactor = interactor,
|
||||
)
|
||||
|
||||
doAnswer {
|
||||
(it.arguments[0] as Boolean).let { animating -> isAnimatingSurface = animating }
|
||||
}
|
||||
.whenever(interactor)
|
||||
.setAnimatingSurface(anyBoolean())
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
animatorTestRule.advanceTimeBy(1000.toLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotAnimating_setParamsWithNoAnimation() {
|
||||
underTest.viewParams =
|
||||
KeyguardSurfaceBehindModel(
|
||||
alpha = 0.3f,
|
||||
translationY = 300f,
|
||||
)
|
||||
|
||||
// A surface has not yet been provided, so we shouldn't have set animating to false OR true
|
||||
// just yet.
|
||||
assertNull(isAnimatingSurface)
|
||||
|
||||
underTest.applyParamsToSurface(remoteAnimationTarget)
|
||||
|
||||
// We should now explicitly not be animating the surface.
|
||||
assertFalse(checkNotNull(isAnimatingSurface))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnimating_paramsThenSurfaceProvided() {
|
||||
underTest.viewParams =
|
||||
KeyguardSurfaceBehindModel(
|
||||
animateFromAlpha = 0f,
|
||||
alpha = 0.3f,
|
||||
animateFromTranslationY = 0f,
|
||||
translationY = 300f,
|
||||
)
|
||||
|
||||
// A surface has not yet been provided, so we shouldn't have set animating to false OR true
|
||||
// just yet.
|
||||
assertNull(isAnimatingSurface)
|
||||
|
||||
underTest.applyParamsToSurface(remoteAnimationTarget)
|
||||
|
||||
// We should now be animating the surface.
|
||||
assertTrue(checkNotNull(isAnimatingSurface))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnimating_surfaceThenParamsProvided() {
|
||||
underTest.applyParamsToSurface(remoteAnimationTarget)
|
||||
|
||||
// The default params (which do not animate) should have been applied, so we're explicitly
|
||||
// NOT animating yet.
|
||||
assertFalse(checkNotNull(isAnimatingSurface))
|
||||
|
||||
underTest.viewParams =
|
||||
KeyguardSurfaceBehindModel(
|
||||
animateFromAlpha = 0f,
|
||||
alpha = 0.3f,
|
||||
animateFromTranslationY = 0f,
|
||||
translationY = 300f,
|
||||
)
|
||||
|
||||
// We should now be animating the surface.
|
||||
assertTrue(checkNotNull(isAnimatingSurface))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnimating_thenReleased_animatingIsFalse() {
|
||||
underTest.viewParams =
|
||||
KeyguardSurfaceBehindModel(
|
||||
animateFromAlpha = 0f,
|
||||
alpha = 0.3f,
|
||||
animateFromTranslationY = 0f,
|
||||
translationY = 300f,
|
||||
)
|
||||
underTest.applyParamsToSurface(remoteAnimationTarget)
|
||||
|
||||
assertTrue(checkNotNull(isAnimatingSurface))
|
||||
|
||||
underTest.notifySurfaceReleased()
|
||||
|
||||
// Releasing the surface should immediately cancel animators.
|
||||
assertFalse(checkNotNull(isAnimatingSurface))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.binder
|
||||
|
||||
import android.app.IActivityTaskManager
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RoboPilotTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
|
||||
private lateinit var underTest: WindowManagerLockscreenVisibilityManager
|
||||
private lateinit var executor: FakeExecutor
|
||||
|
||||
@Mock private lateinit var activityTaskManagerService: IActivityTaskManager
|
||||
|
||||
@Mock private lateinit var keyguardStateController: KeyguardStateController
|
||||
|
||||
@Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
executor = FakeExecutor(FakeSystemClock())
|
||||
|
||||
underTest =
|
||||
WindowManagerLockscreenVisibilityManager(
|
||||
executor = executor,
|
||||
activityTaskManagerService = activityTaskManagerService,
|
||||
keyguardStateController = keyguardStateController,
|
||||
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLockscreenVisible_andAodVisible() {
|
||||
underTest.setLockscreenShown(true)
|
||||
underTest.setAodVisible(true)
|
||||
|
||||
verify(activityTaskManagerService).setLockScreenShown(true, true)
|
||||
verifyNoMoreInteractions(activityTaskManagerService)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible() {
|
||||
underTest.setLockscreenShown(true)
|
||||
underTest.setAodVisible(true)
|
||||
|
||||
verify(activityTaskManagerService).setLockScreenShown(true, true)
|
||||
verifyNoMoreInteractions(activityTaskManagerService)
|
||||
|
||||
underTest.setSurfaceBehindVisibility(true)
|
||||
|
||||
verify(activityTaskManagerService).keyguardGoingAway(anyInt())
|
||||
verifyNoMoreInteractions(activityTaskManagerService)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway() {
|
||||
underTest.setLockscreenShown(false)
|
||||
underTest.setAodVisible(false)
|
||||
|
||||
verify(activityTaskManagerService).setLockScreenShown(false, false)
|
||||
verifyNoMoreInteractions(activityTaskManagerService)
|
||||
|
||||
underTest.setSurfaceBehindVisibility(true)
|
||||
|
||||
verifyNoMoreInteractions(activityTaskManagerService)
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.RoboPilotTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
|
||||
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
|
||||
import com.android.systemui.shade.data.repository.FakeShadeRepository
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager
|
||||
@@ -83,16 +83,21 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() {
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
shadeRepository = FakeShadeRepository()
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractor(
|
||||
transitionRepository,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
val keyguardInteractor =
|
||||
KeyguardInteractorFactory.create(
|
||||
repository = keyguardRepository,
|
||||
featureFlags = featureFlags,
|
||||
)
|
||||
.keyguardInteractor
|
||||
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
scope = testScope.backgroundScope,
|
||||
repository = transitionRepository,
|
||||
keyguardInteractor = keyguardInteractor,
|
||||
)
|
||||
.keyguardTransitionInteractor
|
||||
|
||||
underTest =
|
||||
FingerprintViewModel(
|
||||
context,
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepos
|
||||
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
|
||||
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.StatusBarState
|
||||
@@ -98,15 +98,20 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
bouncerRepository = it.bouncerRepository
|
||||
}
|
||||
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
scope = testScope.backgroundScope,
|
||||
repository = transitionRepository,
|
||||
keyguardInteractor = keyguardInteractor,
|
||||
)
|
||||
.keyguardTransitionInteractor
|
||||
|
||||
underTest =
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
lockscreenColorResId,
|
||||
alternateBouncerResId,
|
||||
KeyguardTransitionInteractor(
|
||||
transitionRepository,
|
||||
testScope.backgroundScope,
|
||||
),
|
||||
transitionInteractor,
|
||||
UdfpsKeyguardInteractor(
|
||||
configRepository,
|
||||
BurnInInteractor(
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.InstanceId
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||
import com.android.keyguard.TestScopeProvider
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
@@ -37,6 +38,7 @@ import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
@@ -66,7 +68,6 @@ import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertTrue
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
@@ -152,7 +153,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
|
||||
debugLogger,
|
||||
mediaFlags,
|
||||
keyguardUpdateMonitor,
|
||||
KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
|
||||
KeyguardTransitionInteractorFactory.create(
|
||||
scope = TestScopeProvider.getTestScope().backgroundScope,
|
||||
repository = transitionRepository,
|
||||
)
|
||||
.keyguardTransitionInteractor,
|
||||
globalSettings
|
||||
)
|
||||
verify(configurationController).addCallback(capture(configListener))
|
||||
|
||||
@@ -35,6 +35,8 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
|
||||
|
||||
import android.service.trust.TrustAgentService;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
@@ -73,6 +75,8 @@ import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dreams.DreamOverlayStateController;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.navigationbar.TaskbarDelegate;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
@@ -201,7 +205,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
mBouncerView,
|
||||
mAlternateBouncerInteractor,
|
||||
mUdfpsOverlayInteractor,
|
||||
mActivityStarter) {
|
||||
mActivityStarter,
|
||||
mock(KeyguardTransitionInteractor.class),
|
||||
StandardTestDispatcher(null, null),
|
||||
() -> mock(WindowManagerLockscreenVisibilityInteractor.class)) {
|
||||
@Override
|
||||
public ViewRootImpl getViewRootImpl() {
|
||||
return mViewRootImpl;
|
||||
@@ -701,7 +708,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
mBouncerView,
|
||||
mAlternateBouncerInteractor,
|
||||
mUdfpsOverlayInteractor,
|
||||
mActivityStarter) {
|
||||
mActivityStarter,
|
||||
mock(KeyguardTransitionInteractor.class),
|
||||
StandardTestDispatcher(null, null),
|
||||
() -> mock(WindowManagerLockscreenVisibilityInteractor.class)) {
|
||||
@Override
|
||||
public ViewRootImpl getViewRootImpl() {
|
||||
return mViewRootImpl;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.data.repository
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class FakeKeyguardSurfaceBehindRepository : KeyguardSurfaceBehindRepository {
|
||||
private val _isAnimatingSurface = MutableStateFlow(false)
|
||||
override val isAnimatingSurface = _isAnimatingSurface.asStateFlow()
|
||||
|
||||
override fun setAnimatingSurface(animating: Boolean) {
|
||||
_isAnimatingSurface.value = animating
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
/**
|
||||
@@ -30,18 +32,36 @@ object KeyguardTransitionInteractorFactory {
|
||||
fun create(
|
||||
scope: CoroutineScope,
|
||||
repository: KeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
|
||||
keyguardInteractor: KeyguardInteractor =
|
||||
KeyguardInteractorFactory.create().keyguardInteractor,
|
||||
fromLockscreenTransitionInteractor: Lazy<FromLockscreenTransitionInteractor> = Lazy {
|
||||
mock<FromLockscreenTransitionInteractor>()
|
||||
},
|
||||
fromPrimaryBouncerTransitionInteractor: Lazy<FromPrimaryBouncerTransitionInteractor> =
|
||||
Lazy {
|
||||
mock<FromPrimaryBouncerTransitionInteractor>()
|
||||
},
|
||||
): WithDependencies {
|
||||
return WithDependencies(
|
||||
repository = repository,
|
||||
keyguardInteractor = keyguardInteractor,
|
||||
fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor,
|
||||
fromPrimaryBouncerTransitionInteractor = fromPrimaryBouncerTransitionInteractor,
|
||||
KeyguardTransitionInteractor(
|
||||
scope = scope,
|
||||
repository = repository,
|
||||
keyguardInteractor = { keyguardInteractor },
|
||||
fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor,
|
||||
fromPrimaryBouncerTransitionInteractor = fromPrimaryBouncerTransitionInteractor,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
data class WithDependencies(
|
||||
val repository: KeyguardTransitionRepository,
|
||||
val keyguardInteractor: KeyguardInteractor,
|
||||
val fromLockscreenTransitionInteractor: Lazy<FromLockscreenTransitionInteractor>,
|
||||
val fromPrimaryBouncerTransitionInteractor: Lazy<FromPrimaryBouncerTransitionInteractor>,
|
||||
val keyguardTransitionInteractor: KeyguardTransitionInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user