From a1322dee86a69328a4bdfe070f299b1eb45d48ed Mon Sep 17 00:00:00 2001 From: Josh Tsuji Date: Wed, 2 Feb 2022 15:20:36 -0500 Subject: [PATCH] Uses ActivityLaunchAnimator for occlusion animations. This is used when the lock screen is visible and an activity occludes it, such as the camera. Currently, the camera launches behind the lock screen and then the lock screen janks out of the way to show the camera. There are currently some pretty severe performance issues, but I'm not sure yet whether that's due to the new launch animator, or the fact that the old animation is so janky that you can't really tell. I'm investigating that, but we should get this into the build to start working out any bugs with the remote occlude animations. This requires remote occlude animations to be enabled: adb shell setprop persist.wm.enable_remote_keyguard_animation 2 Test: atest SystemUITests Bug: 197312643 Change-Id: Id801e44c25c6e2340b4269d215b9a1c056f0ceff --- .../animation/ActivityLaunchAnimator.kt | 2 + .../systemui/animation/LaunchAnimator.kt | 3 + .../keyguard/KeyguardUpdateMonitor.java | 7 + .../systemui/keyguard/KeyguardService.java | 53 ++---- .../keyguard/KeyguardViewMediator.java | 163 +++++++++++++++++- .../keyguard/dagger/KeyguardModule.java | 8 +- .../keyguard/KeyguardViewMediatorTest.java | 5 +- 7 files changed, 196 insertions(+), 45 deletions(-) diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 08d217d15a5ae..4540b77d0a40b 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -301,6 +301,7 @@ class ActivityLaunchAnimator( * The intent was started. If [willAnimate] is false, nothing else will happen and the * animation will not be started. */ + @JvmDefault fun onIntentStarted(willAnimate: Boolean) {} /** @@ -308,6 +309,7 @@ class ActivityLaunchAnimator( * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called * before the cancellation. */ + @JvmDefault fun onLaunchAnimationCancelled() {} } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index ebe96ebf29889..77386cf2ff10a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -100,9 +100,11 @@ class LaunchAnimator( * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding * fully above the [launchContainer]. */ + @JvmDefault fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {} /** The animation made progress and the expandable view [state] should be updated. */ + @JvmDefault fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {} /** @@ -110,6 +112,7 @@ class LaunchAnimator( * called previously. This is typically used to clean up the resources initialized when the * animation was started. */ + @JvmDefault fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {} } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f2d0427c39d3e..451fa63af398b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -679,6 +679,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE); } + /** + * Whether the secure camera is currently showing over the keyguard. + */ + public boolean isSecureCameraLaunchedOverKeyguard() { + return mSecureCameraLaunched; + } + /** * @return a cached version of DreamManager.isDreaming() */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index e88011e0d3fe4..88555edd1e8b9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -233,10 +233,13 @@ public class KeyguardService extends Service { mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; mShellTransitions = shellTransitions; + } - if (shellTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS) { - // Nothing here. Initialization for this happens in onCreate. - } else { + @Override + public void onCreate() { + ((SystemUIApplication) getApplication()).startServicesIfNeeded(); + + if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); if (sEnableRemoteKeyguardGoingAwayAnimation) { final RemoteAnimationAdapter exitAnimationAdapter = @@ -248,22 +251,19 @@ public class KeyguardService extends Service { } if (sEnableRemoteKeyguardOccludeAnimation) { final RemoteAnimationAdapter occludeAnimationAdapter = - new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0); + new RemoteAnimationAdapter( + mKeyguardViewMediator.getOccludeAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter); + + final RemoteAnimationAdapter unoccludeAnimationAdapter = + new RemoteAnimationAdapter( + mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, - occludeAnimationAdapter); + unoccludeAnimationAdapter); } ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay( DEFAULT_DISPLAY, definition); - } - } - - @Override - public void onCreate() { - ((SystemUIApplication) getApplication()).startServicesIfNeeded(); - - if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { return; } if (sEnableRemoteKeyguardGoingAwayAnimation) { @@ -354,33 +354,6 @@ public class KeyguardService extends Service { } }; - private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner = - new IRemoteAnimationRunner.Stub() { - @Override // Binder interface - public void onAnimationStart(@WindowManager.TransitionOldType int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback) { - Slog.d(TAG, "mOccludeAnimationRunner.onAnimationStart: transit=" + transit); - try { - if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { - mBinder.setOccluded(true /* isOccluded */, true /* animate */); - } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) { - mBinder.setOccluded(false /* isOccluded */, false /* animate */); - } - // TODO(bc-unlock): Implement (un)occlude animation. - finishedCallback.onAnimationFinished(); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException"); - } - } - - @Override // Binder interface - public void onAnimationCancelled() { - } - }; - final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() { @Override public void startAnimation(IBinder transition, TransitionInfo info, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index fd2c6dd3ca361..0f08a1863e081 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -77,11 +77,13 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.jank.InteractionJankMonitor; @@ -89,6 +91,7 @@ import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardStateCallback; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardConstants; @@ -102,7 +105,10 @@ import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; +import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.Interpolators; +import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -375,6 +381,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private int mUnlockSoundId; private int mTrustedSoundId; private int mLockSoundStreamId; + private final float mPowerButtonY; + private final float mWindowCornerRadius; + /** * The animation used for hiding keyguard. This is used to fetch the animation timings if * WindowManager is not providing us with them. @@ -815,6 +824,109 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } }; + /** + * Animation launch controller for activities that occlude the keyguard. + */ + private final ActivityLaunchAnimator.Controller mOccludeAnimationController = + new ActivityLaunchAnimator.Controller() { + @Override + public void onLaunchAnimationStart(boolean isExpandingFullyAbove) { + setOccluded(true /* occluded */, false /* animate */); + } + + @Override + public void onLaunchAnimationCancelled() { + setOccluded(true /* occluded */, false /* animate */); + } + + @NonNull + @Override + public ViewGroup getLaunchContainer() { + return ((ViewGroup) mKeyguardViewControllerLazy.get() + .getViewRootImpl().getView()); + } + + @Override + public void setLaunchContainer(@NonNull ViewGroup launchContainer) { + // No-op, launch container is always the shade. + Log.wtf(TAG, "Someone tried to change the launch container for the " + + "ActivityLaunchAnimator, which should never happen."); + } + + @NonNull + @Override + public LaunchAnimator.State createAnimatorState() { + final int width = getLaunchContainer().getWidth(); + final int height = getLaunchContainer().getHeight(); + + final float initialHeight = height / 3f; + final float initialWidth = width / 3f; + + if (mUpdateMonitor.isSecureCameraLaunchedOverKeyguard()) { + // Start the animation near the power button, at one-third size, since the + // camera was launched from the power button. + return new LaunchAnimator.State( + (int) (mPowerButtonY - initialHeight / 2f) /* top */, + (int) (mPowerButtonY + initialHeight / 2f) /* bottom */, + (int) (width - initialWidth) /* left */, + width /* right */, + mWindowCornerRadius, mWindowCornerRadius); + } else { + // Start the animation in the center of the screen, scaled down. + return new LaunchAnimator.State( + height / 2, height / 2, width / 2, width / 2, + mWindowCornerRadius, mWindowCornerRadius); + } + } + }; + + /** + * Animation controller for activities that unocclude the keyguard. This will play the launch + * animation in reverse. + */ + private final ActivityLaunchAnimator.Controller mUnoccludeAnimationController = + new ActivityLaunchAnimator.Controller() { + @Override + public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { + setOccluded(false /* isOccluded */, false /* animate */); + } + + @Override + public void onLaunchAnimationCancelled() { + setOccluded(false /* isOccluded */, false /* animate */); + } + + @NonNull + @Override + public ViewGroup getLaunchContainer() { + return ((ViewGroup) mKeyguardViewControllerLazy.get() + .getViewRootImpl().getView()); + } + + @Override + public void setLaunchContainer(@NonNull ViewGroup launchContainer) { + // No-op, launch container is always the shade. + Log.wtf(TAG, "Someone tried to change the launch container for the " + + "ActivityLaunchAnimator, which should never happen."); + } + + @NonNull + @Override + public LaunchAnimator.State createAnimatorState() { + final int width = getLaunchContainer().getWidth(); + final int height = getLaunchContainer().getHeight(); + + // TODO(b/207399883): Unocclude animation. This currently ends instantly. + return new LaunchAnimator.State( + 0, height, 0, width, mWindowCornerRadius, mWindowCornerRadius); + } + }; + + private IRemoteAnimationRunner mOccludeAnimationRunner = + new ActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); + private IRemoteAnimationRunner mUnoccludeAnimationRunner = + new ActivityLaunchRemoteAnimationRunner(mUnoccludeAnimationController); + private DeviceConfigProxy mDeviceConfig; private DozeParameters mDozeParameters; @@ -824,6 +936,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private boolean mWallpaperSupportsAmbientMode; private ScreenOnCoordinator mScreenOnCoordinator; + private Lazy mActivityLaunchAnimator; + /** * Injected constructor. See {@link KeyguardModule}. */ @@ -850,7 +964,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - Lazy notificationShadeWindowControllerLazy) { + Lazy notificationShadeWindowControllerLazy, + Lazy activityLaunchAnimator) { super(context); mFalsingCollector = falsingCollector; mLockPatternUtils = lockPatternUtils; @@ -890,6 +1005,12 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mScreenOffAnimationController = screenOffAnimationController; mInteractionJankMonitor = interactionJankMonitor; mDreamOverlayStateController = dreamOverlayStateController; + + mActivityLaunchAnimator = activityLaunchAnimator; + + mPowerButtonY = context.getResources().getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y); + mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); } public void userActivity() { @@ -1440,6 +1561,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Trace.endSection(); } + public IRemoteAnimationRunner getOccludeAnimationRunner() { + return mOccludeAnimationRunner; + } + + public IRemoteAnimationRunner getUnoccludeAnimationRunner() { + return mUnoccludeAnimationRunner; + } + public boolean isHiding() { return mHiding; } @@ -2868,4 +2997,36 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, return mMessage; } } + + /** + * Implementation of RemoteAnimationRunner that creates a new + * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the + * remote animation methods to that runner. + */ + private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub { + + private final ActivityLaunchAnimator.Controller mActivityLaunchController; + @Nullable private ActivityLaunchAnimator.Runner mRunner; + + ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) { + mActivityLaunchController = controller; + } + + @Override + public void onAnimationCancelled() throws RemoteException { + if (mRunner != null) { + mRunner.onAnimationCancelled(); + } + } + + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) + throws RemoteException { + mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController); + mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index b49b49cbbb6db..195ef1a84c43d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -32,6 +32,7 @@ import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.mediator.ScreenOnCoordinator; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; @@ -102,7 +103,8 @@ public class KeyguardModule { ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - Lazy notificationShadeWindowController) { + Lazy notificationShadeWindowController, + Lazy activityLaunchAnimator) { return new KeyguardViewMediator( context, falsingCollector, @@ -128,8 +130,8 @@ public class KeyguardModule { screenOnCoordinator, interactionJankMonitor, dreamOverlayStateController, - notificationShadeWindowController - ); + notificationShadeWindowController, + activityLaunchAnimator); } @SysUISingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d94e2eee9ffa2..210cb82e16062 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -46,6 +46,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dreams.DreamOverlayStateController; @@ -100,6 +101,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ScreenOnCoordinator mScreenOnCoordinator; private @Mock Lazy mNotificationShadeWindowControllerLazy; private @Mock DreamOverlayStateController mDreamOverlayStateController; + private @Mock ActivityLaunchAnimator mActivityLaunchAnimator; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -205,7 +207,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOnCoordinator, mInteractionJankMonitor, mDreamOverlayStateController, - mNotificationShadeWindowControllerLazy); + mNotificationShadeWindowControllerLazy, + () -> mActivityLaunchAnimator); mViewMediator.start(); } }