diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index cfe44bbbf3c69..3774dacfa5ffc 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -487,6 +487,13 @@ public interface WindowManager extends ViewManager { */ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION = 0x8; + /** + * Transition flag: Keyguard is going away to the launcher, and it needs us to clear the task + * snapshot of the launcher because it has changed something in the Launcher window. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT = 0x16; + /** * Transition flag: App is crashed. * @hide @@ -527,6 +534,7 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION, TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER, TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION, + TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT, TRANSIT_FLAG_APP_CRASHED, TRANSIT_FLAG_OPEN_BEHIND, TRANSIT_FLAG_KEYGUARD_LOCKED, diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 94f633314b4ef..4d07171d3086a 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -48,6 +48,7 @@ public interface WindowManagerPolicyConstants { int KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS = 1 << 1; int KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER = 1 << 2; int KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS = 1 << 3; + int KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT = 1 << 4; // Flags used for indicating whether the internal and/or external input devices // of some type are available. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index a8c286241141e..c90fa4db1588b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -259,7 +259,9 @@ class KeyguardUnlockAnimationController @Inject constructor( * animation plays. */ private var surfaceBehindAlpha = 1f - private var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) + + @VisibleForTesting + var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) private var smartspaceAnimator = ValueAnimator.ofFloat(0f, 1f) /** @@ -422,12 +424,22 @@ class KeyguardUnlockAnimationController @Inject constructor( surfaceBehindRemoteAnimationTarget = target surfaceBehindRemoteAnimationStartTime = startTime - // If we specifically requested that the surface behind be made visible, it means we are - // swiping to unlock. In that case, the surface visibility is tied to the dismiss amount, - // and we'll handle that in onKeyguardDismissAmountChanged(). If we didn't request that, the - // keyguard is being dismissed for a different reason (biometric auth, etc.) and we should - // play a canned animation to make the surface fully visible. - if (!requestedShowSurfaceBehindKeyguard) { + // If we specifically requested that the surface behind be made visible (vs. it being made + // visible because we're unlocking), then we're in the middle of a swipe-to-unlock touch + // gesture and the surface behind the keyguard should be made visible. + if (requestedShowSurfaceBehindKeyguard) { + // Fade in the surface, as long as we're not now flinging. The touch gesture ending in + // a fling during the time it takes the keyguard exit animation to start is an edge + // case race condition, and we'll handle it by playing a canned animation on the + // now-visible surface to finish unlocking. + if (!keyguardStateController.isFlingingToDismissKeyguard) { + fadeInSurfaceBehind() + } else { + playCannedUnlockAnimation() + } + } else { + // The surface was made visible since we're unlocking not from a swipe (fingerprint, + // lock icon long-press, etc). Play the full unlock animation. playCannedUnlockAnimation() } @@ -786,7 +798,6 @@ class KeyguardUnlockAnimationController @Inject constructor( prepareLauncherWorkspaceForUnlockAnimation() } keyguardViewMediator.get().showSurfaceBehindKeyguard() - fadeInSurfaceBehind() } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { // We're no longer past the threshold but we are showing the surface. Animate it diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 29e940f24df6c..b7c224a713987 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -17,6 +17,8 @@ package com.android.systemui.keyguard; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN; @@ -119,7 +121,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -2307,8 +2308,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, int flags = 0; if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock() || mWakeAndUnlocking && !mWallpaperSupportsAmbientMode) { - flags |= WindowManagerPolicyConstants - .KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; + flags |= KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; } if (mKeyguardViewControllerLazy.get().isGoingToNotificationShade() || mWakeAndUnlocking && mWallpaperSupportsAmbientMode) { @@ -2324,6 +2324,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, .KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; } + // If we are unlocking to the launcher, clear the snapshot so that any changes as part + // of the in-window animations are reflected. This is needed even if we're not actually + // playing in-window animations for this particular unlock since a previous unlock might + // have changed the Launcher state. + if (mWakeAndUnlocking + && KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) { + flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; + } + mUpdateMonitor.setKeyguardGoingAway(true); mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(true); @@ -2628,9 +2637,18 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mSurfaceBehindRemoteAnimationRequested = true; try { - ActivityTaskManager.getService().keyguardGoingAway( - WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS - | WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER); + int flags = KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS + | KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; + + // If we are unlocking to the launcher, clear the snapshot so that any changes as part + // of the in-window animations are reflected. This is needed even if we're not actually + // playing in-window animations for this particular unlock since a previous unlock might + // have changed the Launcher state. + if (KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) { + flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; + } + + ActivityTaskManager.getService().keyguardGoingAway(flags); mKeyguardStateController.notifyKeyguardGoingAway(true); } catch (RemoteException e) { mSurfaceBehindRemoteAnimationRequested = false; @@ -2796,12 +2814,6 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking"); mWakeAndUnlocking = true; - // We're going to animate in the Launcher, so ask WM to clear the task snapshot so we don't - // initially display an old snapshot with all of the icons visible. We're System UI, so - // we're allowed to pass in null to ask WM to find the home activity for us to prevent - // needing to IPC to Launcher. - ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(null /* homeActivity */); - keyguardDone(); Trace.endSection(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index fb1a968acceb1..2d8c4d5dceb06 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -17,6 +17,7 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -125,4 +126,69 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished( false /* cancelled */) } + + /** + * If we requested that the surface behind be made visible, and we're not flinging away the + * keyguard, it means that we're swiping to unlock and want the surface visible so it can follow + * the user's touch event as they swipe to unlock. + * + * In this case, we should verify that the surface was made visible via the alpha fade in + * animator, and verify that we did not start the canned animation to animate the surface in + * (since it's supposed to be following the touch events). + */ + @Test + fun fadeInSurfaceBehind_ifRequestedShowSurface_butNotFlinging() { + `when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTarget, + 0 /* startTime */, + true /* requestedShowSurfaceBehindKeyguard */ + ) + + assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning) + assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) + } + + /** + * We requested the surface behind to be made visible, but we're now flinging to dismiss the + * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and + * lifted their finger while we were requesting the surface be made visible. + * + * In this case, we should verify that we are playing the canned unlock animation and not + * simply fading in the surface. + */ + @Test + fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() { + `when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTarget, + 0 /* startTime */, + true /* requestedShowSurfaceBehindKeyguard */ + ) + + assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) + assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning) + } + + /** + * We never requested the surface behind to be made visible, which means no swiping to unlock + * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock, + * long press on the lock icon, etc). + * + * In this case, we should verify that we are playing the canned unlock animation and not + * simply fading in the surface. + */ + @Test + fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() { + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTarget, + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ + ) + + assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) + assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning) + } } \ No newline at end of file diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 350cb7ae09936..11d1cae70d6f4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -64,6 +64,8 @@ import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; import static android.provider.Settings.System.FONT_SCALE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; +import static android.view.WindowManager.TRANSIT_WAKE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; @@ -3397,6 +3399,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long token = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + // Keyguard asked us to clear the home task snapshot before going away, so do that. + if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { + mActivityClientController.invalidateHomeTaskSnapshot(null /* token */); + } + mRootWindowContainer.forAllDisplays(displayContent -> { mKeyguardController.keyguardGoingAway(displayContent.getDisplayId(), flags); }); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 4f015d8d77a02..5410dd8508f14 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; @@ -1258,6 +1259,9 @@ public class AppTransition implements Dump { "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER")); sFlagToString.add(new Pair<>(TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION, "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION")); + sFlagToString.add(new Pair<>( + TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT, + "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_WITH_IN_WINDOW_ANIMATIONS")); sFlagToString.add(new Pair<>(TRANSIT_FLAG_APP_CRASHED, "TRANSIT_FLAG_APP_CRASHED")); sFlagToString.add(new Pair<>(TRANSIT_FLAG_OPEN_BEHIND, diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index ddcaed7b65e79..03ac7aafb482b 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; @@ -32,6 +33,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; @@ -314,6 +316,10 @@ class KeyguardController { if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS) != 0) { result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; } + if ((keyguardGoingAwayFlags + & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { + result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; + } return result; }