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
This commit is contained in:
Josh Tsuji
2022-02-02 15:20:36 -05:00
parent 5f73621d01
commit a1322dee86
7 changed files with 196 additions and 45 deletions

View File

@@ -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() {}
}

View File

@@ -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) {}
}

View File

@@ -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()
*/

View File

@@ -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,

View File

@@ -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<ActivityLaunchAnimator> mActivityLaunchAnimator;
/**
* Injected constructor. See {@link KeyguardModule}.
*/
@@ -850,7 +964,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) {
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> 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);
}
}
}

View File

@@ -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> notificationShadeWindowController) {
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -128,8 +130,8 @@ public class KeyguardModule {
screenOnCoordinator,
interactionJankMonitor,
dreamOverlayStateController,
notificationShadeWindowController
);
notificationShadeWindowController,
activityLaunchAnimator);
}
@SysUISingleton

View File

@@ -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<NotificationShadeWindowController> 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();
}
}