diff options
| author | 2021-05-31 08:54:58 +0000 | |
|---|---|---|
| committer | 2021-05-31 08:54:58 +0000 | |
| commit | 2c52d52f6d5dd5715cdf012ad0e4129944fc4ba6 (patch) | |
| tree | 55b190d81d3021327accd52004d35db779adc02d | |
| parent | ad1cfd0db8be9d88343260787a37fca6064f9106 (diff) | |
| parent | f90ca12d50b3b4aed3ffc32af68e1e1bdb241043 (diff) | |
Merge "Animate app launches from lockscreen" into sc-dev
9 files changed, 251 insertions, 76 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 2b8773783048..4f179c46a3d9 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -32,7 +32,10 @@ import kotlin.math.roundToInt * A class that allows activities to be started in a seamless way from a view that is transforming * nicely into the starting window. */ -class ActivityLaunchAnimator(context: Context) { +class ActivityLaunchAnimator( + private val keyguardHandler: KeyguardHandler, + context: Context +) { private val TAG = this::class.java.simpleName companion object { @@ -104,15 +107,22 @@ class ActivityLaunchAnimator(context: Context) { Log.d(TAG, "Starting intent with a launch animation") val runner = Runner(controller) - val animationAdapter = RemoteAnimationAdapter( - runner, - ANIMATION_DURATION, - ANIMATION_DURATION - 150 /* statusBarTransitionDelay */ - ) + val isOnKeyguard = keyguardHandler.isOnKeyguard() + + // Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard. + val animationAdapter = if (!isOnKeyguard) { + RemoteAnimationAdapter( + runner, + ANIMATION_DURATION, + ANIMATION_DURATION - 150 /* statusBarTransitionDelay */ + ) + } else { + null + } // Register the remote animation for the given package to also animate trampoline // activity launches. - if (packageName != null) { + if (packageName != null && animationAdapter != null) { try { ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart( packageName, animationAdapter) @@ -122,20 +132,30 @@ class ActivityLaunchAnimator(context: Context) { } val launchResult = intentStarter(animationAdapter) - val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT || - launchResult == ActivityManager.START_SUCCESS - Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate") + // Only animate if the app is not already on top and will be opened, unless we are on the + // keyguard. + val willAnimate = + launchResult == ActivityManager.START_TASK_TO_FRONT || + launchResult == ActivityManager.START_SUCCESS || + (launchResult == ActivityManager.START_DELIVERED_TO_TOP && isOnKeyguard) + + Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate isOnKeyguard=$isOnKeyguard") controller.callOnIntentStartedOnMainThread(willAnimate) // If we expect an animation, post a timeout to cancel it in case the remote animation is // never started. if (willAnimate) { runner.postTimeout() + + // Hide the keyguard using the launch animation instead of the default unlock animation. + if (isOnKeyguard) { + keyguardHandler.hideKeyguardWithAnimation(runner) + } } } - internal fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) { + private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) { if (Looper.myLooper() != Looper.getMainLooper()) { this.launchContainer.context.mainExecutor.execute { this.onIntentStarted(willAnimate) @@ -179,6 +199,14 @@ class ActivityLaunchAnimator(context: Context) { fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int } + interface KeyguardHandler { + /** Whether we are currently on the keyguard or not. */ + fun isOnKeyguard(): Boolean + + /** Hide the keyguard and animate using [runner]. */ + fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) + } + /** * A controller that takes care of applying the animation to an expanding view. * @@ -337,17 +365,17 @@ class ActivityLaunchAnimator(context: Context) { override fun onAnimationStart( @WindowManager.TransitionOldType transit: Int, - remoteAnimationTargets: Array<out RemoteAnimationTarget>, - remoteAnimationWallpaperTargets: Array<out RemoteAnimationTarget>, - remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>, - iRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback + apps: Array<out RemoteAnimationTarget>?, + wallpapers: Array<out RemoteAnimationTarget>?, + nonApps: Array<out RemoteAnimationTarget>?, + iCallback: IRemoteAnimationFinishedCallback? ) { removeTimeout() // The animation was started too late and we already notified the controller that it // timed out. if (timedOut) { - invokeCallback(iRemoteAnimationFinishedCallback) + iCallback?.invoke() return } @@ -358,30 +386,29 @@ class ActivityLaunchAnimator(context: Context) { } context.mainExecutor.execute { - startAnimation(remoteAnimationTargets, remoteAnimationNonAppTargets, - iRemoteAnimationFinishedCallback) + startAnimation(apps, nonApps, iCallback) } } private fun startAnimation( - remoteAnimationTargets: Array<out RemoteAnimationTarget>, - remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>, - iCallback: IRemoteAnimationFinishedCallback + apps: Array<out RemoteAnimationTarget>?, + nonApps: Array<out RemoteAnimationTarget>?, + iCallback: IRemoteAnimationFinishedCallback? ) { Log.d(TAG, "Remote animation started") - val window = remoteAnimationTargets.firstOrNull { + val window = apps?.firstOrNull { it.mode == RemoteAnimationTarget.MODE_OPENING } if (window == null) { Log.d(TAG, "Aborting the animation as no window is opening") removeTimeout() - invokeCallback(iCallback) + iCallback?.invoke() controller.onLaunchAnimationCancelled() return } - val navigationBar = remoteAnimationNonAppTargets.firstOrNull { + val navigationBar = nonApps?.firstOrNull { it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR } @@ -439,7 +466,7 @@ class ActivityLaunchAnimator(context: Context) { override fun onAnimationEnd(animation: Animator?) { Log.d(TAG, "Animation ended") - invokeCallback(iCallback) + iCallback?.invoke() controller.onLaunchAnimationEnd(isExpandingFullyAbove) } }) @@ -519,8 +546,11 @@ class ActivityLaunchAnimator(context: Context) { ) // The scale will also be applied to the corner radius, so we divide by the scale to - // keep the original radius. - val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius) / scale + // keep the original radius. We use the max of (topCornerRadius, bottomCornerRadius) to + // make sure that the window does not draw itself behind the expanding view. This is + // especially important for lock screen animations, where the window is not clipped by + // the shade. + val cornerRadius = maxOf(state.topCornerRadius, state.bottomCornerRadius) / scale val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash) .withAlpha(1f) .withMatrix(matrix) @@ -585,9 +615,9 @@ class ActivityLaunchAnimator(context: Context) { } } - private fun invokeCallback(iCallback: IRemoteAnimationFinishedCallback) { + private fun IRemoteAnimationFinishedCallback.invoke() { try { - iCallback.onAnimationFinished() + onAnimationFinished() } catch (e: RemoteException) { e.printStackTrace() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 666afed41c35..9b372b8bdec6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -70,7 +70,7 @@ public class KeyguardService extends Service { /** * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY */ - static boolean sEnableRemoteKeyguardAnimation = + public static boolean sEnableRemoteKeyguardAnimation = SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false); private final KeyguardViewMediator mKeyguardViewMediator; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 36a0acc6fb4c..387f24f88b63 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -52,6 +52,7 @@ import android.media.SoundPool; import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -71,6 +72,7 @@ import android.util.Slog; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.View; @@ -426,6 +428,11 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, */ private IRemoteAnimationFinishedCallback mSurfaceBehindRemoteAnimationFinishedCallback; + /** + * The animation runner to use for the next exit animation. + */ + private IRemoteAnimationRunner mKeyguardExitAnimationRunner; + private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -1605,6 +1612,16 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, Trace.endSection(); } + /** Hide the keyguard and let {@code runner} handle the animation. */ + public void hideWithAnimation(IRemoteAnimationRunner runner) { + if (!mShowing) { + return; + } + + mKeyguardExitAnimationRunner = runner; + hideLocked(); + } + public boolean isSecure() { return isSecure(KeyguardUpdateMonitor.getCurrentUser()); } @@ -2050,6 +2067,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, // TODO: We should stop it early by disabling the swipe up flow. Right now swipe up // still completes and makes the screen blank. if (DEBUG) Log.d(TAG, "Split system user, quit unlocking."); + mKeyguardExitAnimationRunner = null; return; } mHiding = true; @@ -2103,9 +2121,37 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, playSounds(false); } + IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner; + mKeyguardExitAnimationRunner = null; + + if (KeyguardService.sEnableRemoteKeyguardAnimation && runner != null + && finishedCallback != null) { + // Wrap finishedCallback to clean up the keyguard state once the animation is done. + IRemoteAnimationFinishedCallback callback = + new IRemoteAnimationFinishedCallback() { + @Override + public void onAnimationFinished() throws RemoteException { + finishedCallback.onAnimationFinished(); + onKeyguardExitFinished(); + mKeyguardViewControllerLazy.get().hide(0 /* startTime */, + 0 /* fadeoutDuration */); + } + + @Override + public IBinder asBinder() { + return finishedCallback.asBinder(); + } + }; + try { + runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps, + wallpapers, nonApps, callback); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onAnimationStart", e); + } + // When remaining on the shade, there's no need to do a fancy remote animation, // it will dismiss the panel in that case. - if (KeyguardService.sEnableRemoteKeyguardAnimation + } else if (KeyguardService.sEnableRemoteKeyguardAnimation && !mStatusBarStateController.leaveOpenOnKeyguardHide() && apps != null && apps.length > 0) { mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback; @@ -2115,9 +2161,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation( apps[0], startTime, mSurfaceBehindRemoteAnimationRequested); } else { - setShowingLocked(false); - mWakeAndUnlocking = false; - mDismissCallbackRegistry.notifyDismissSucceeded(); mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration); // TODO(bc-animation): When remote animation is enabled for keyguard exit animation, @@ -2140,8 +2183,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, SyncRtSurfaceTransactionApplier.SurfaceParams params = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( primary.leash) - .withAlpha(animation.getAnimatedFraction()) - .build(); + .withAlpha(animation.getAnimatedFraction()) + .build(); applier.scheduleApply(params); }); anim.addListener(new AnimatorListenerAdapter() { @@ -2165,16 +2208,24 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, }); anim.start(); }); - resetKeyguardDonePendingLocked(); - mHideAnimationRun = false; - adjustStatusBarLocked(); - sendUserPresentBroadcast(); + + onKeyguardExitFinished(); } } Trace.endSection(); } + private void onKeyguardExitFinished() { + setShowingLocked(false); + mWakeAndUnlocking = false; + mDismissCallbackRegistry.notifyDismissSucceeded(); + resetKeyguardDonePendingLocked(); + mHideAnimationRun = false; + adjustStatusBarLocked(); + sendUserPresentBroadcast(); + } + /** * Whether we're currently animating between the keyguard and the app/launcher surface behind * it, or will be shortly (which happens if we started a fling to dismiss the keyguard). @@ -2211,21 +2262,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, // Block the panel from expanding, in case we were doing a swipe to dismiss gesture. mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch(); final boolean wasShowing = mShowing; - setShowingLocked(false); - - mWakeAndUnlocking = false; - mDismissCallbackRegistry.notifyDismissSucceeded(); + onKeyguardExitFinished(); if (mKeyguardStateController.isDismissingFromSwipe() || !wasShowing) { mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation(); } finishSurfaceBehindRemoteAnimation(); - - resetKeyguardDonePendingLocked(); - mHideAnimationRun = false; - adjustStatusBarLocked(); - sendUserPresentBroadcast(); mSurfaceBehindRemoteAnimationRequested = false; mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index b237f6f0a46e..3e2bcf9562b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -211,6 +211,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return super.onInterceptTouchEvent(ev); } + /** + * Called by the TouchHandler when this view is tapped. This will be called for actual taps + * only, i.e. taps that have been filtered by the FalsingManager. + */ + public void onTap() {} + /** Sets the last action up time this view was touched. */ void setLastActionUpTime(long eventTime) { mLastActionUpTime = eventTime; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index 0a63e19acc1a..c4961029dc33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -118,7 +118,11 @@ public class ActivatableNotificationViewController if (ev.getAction() == MotionEvent.ACTION_UP) { // If this is a false tap, capture the even so it doesn't result in a click. - return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY); + boolean falseTap = mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY); + if (!falseTap && v instanceof ActivatableNotificationView) { + ((ActivatableNotificationView) v).onTap(); + } + return falseTap; } return result; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index ced284e21963..c6753b3b42f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2291,6 +2291,23 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public void onTap() { + // This notification will expand and animates into the content activity, so we disable the + // ripple. We will restore its value once the tap/click is actually performed. + if (mEntry.getSbn().getNotification().contentIntent != null) { + setRippleAllowed(false); + } + } + + @Override + public boolean performClick() { + // We force-disabled the ripple in onTap. When this method is called, the code drawing the + // ripple will already have been called so we can restore its value now. + updateRippleAllowed(); + return super.performClick(); + } + + @Override public int getIntrinsicHeight() { if (isUserLocked()) { return getActualHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c27497e43400..2396272e30e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -105,6 +105,7 @@ import android.util.Log; import android.util.MathUtils; import android.util.Slog; import android.view.Display; +import android.view.IRemoteAnimationRunner; import android.view.IWindowManager; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; @@ -164,6 +165,7 @@ import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.fragments.ExtensionFragmentListener; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.DismissCallbackRegistry; +import com.android.systemui.keyguard.KeyguardService; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; @@ -267,7 +269,8 @@ public class StatusBar extends SystemUI implements DemoMode, OnHeadsUpChangedListener, CommandQueue.Callbacks, ColorExtractor.OnColorsChangedListener, ConfigurationListener, StatusBarStateController.StateListener, - LifecycleOwner, BatteryController.BatteryStateChangeCallback { + LifecycleOwner, BatteryController.BatteryStateChangeCallback, + ActivityLaunchAnimator.KeyguardHandler { public static final boolean MULTIUSER_DEBUG = false; protected static final int MSG_HIDE_RECENT_APPS = 1020; @@ -1413,7 +1416,7 @@ public class StatusBar extends SystemUI implements DemoMode, private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator = new ActivityLaunchAnimator(mContext); + mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), @@ -2072,10 +2075,45 @@ public class StatusBar extends SystemUI implements DemoMode, } } - /** Whether we should animate an activity launch. */ - public boolean areLaunchAnimationsEnabled() { - // TODO(b/184121838): Support lock screen launch animations. - return mState == StatusBarState.SHADE && !isOccluded(); + /** + * Whether we should animate an activity launch. + * + * Note: This method must be called *before* dismissing the keyguard. + */ + public boolean shouldAnimateLaunch(boolean isActivityIntent) { + // TODO(b/184121838): Support launch animations when occluded. + if (isOccluded()) { + return false; + } + + // Always animate if we are unlocked. + if (!mKeyguardStateController.isShowing()) { + return true; + } + + // If we are locked, only animate if remote unlock animations are enabled and we can dismiss + // the lock screen without challenging the user. We also don't animate non-activity + // launches as they can break the animation. + // TODO(b/184121838): Support non activity launches on the lockscreen. + return isActivityIntent + && KeyguardService.sEnableRemoteKeyguardAnimation + && mKeyguardStateController.canDismissLockScreen(); + } + + @Override + public boolean isOnKeyguard() { + return mKeyguardStateController.isShowing(); + } + + @Override + public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) { + if (!mKeyguardStateController.canDismissLockScreen()) { + Log.wtf(TAG, + "Unable to hide keyguard with animation as the keyguard can't be dismissed"); + return; + } + + mKeyguardViewMediator.hideWithAnimation(runner); } public boolean isDeviceInVrMode() { @@ -2796,8 +2834,9 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity( intent, mLockscreenUserManager.getCurrentUserId()); - ActivityLaunchAnimator.Controller animController = wrapAnimationController( - animationController, dismissShade); + ActivityLaunchAnimator.Controller animController = + shouldAnimateLaunch(true /* isActivityIntent */) ? wrapAnimationController( + animationController, dismissShade) : null; // If we animate, we will dismiss the shade only once the animation is done. This is taken // care of by the StatusBarLaunchAnimationController. @@ -2811,7 +2850,7 @@ public class StatusBar extends SystemUI implements DemoMode, int[] result = new int[]{ActivityManager.START_CANCELED}; mActivityLaunchAnimator.startIntentWithAnimation(animController, - areLaunchAnimationsEnabled(), intent.getPackage(), (adapter) -> { + true /* animate */, intent.getPackage(), (adapter) -> { ActivityOptions options = new ActivityOptions( getActivityOptions(mDisplayId, adapter)); options.setDisallowEnterPictureInPictureWhileLaunching( @@ -4606,6 +4645,7 @@ public class StatusBar extends SystemUI implements DemoMode, mLockscreenUserManager.getCurrentUserId()); boolean collapse = animationController == null; + boolean animate = shouldAnimateLaunch(intent.isActivity()); executeActionDismissingKeyguard(() -> { try { // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the @@ -4615,7 +4655,7 @@ public class StatusBar extends SystemUI implements DemoMode, animationController, this, intent.isActivity()) : null; mActivityLaunchAnimator.startPendingIntentWithAnimation( - controller, areLaunchAnimationsEnabled(), intent.getCreatorPackage(), + controller, animate, intent.getCreatorPackage(), (animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null, null, getActivityOptions(mDisplayId, animationAdapter))); } catch (PendingIntent.CanceledException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 5cd4e13bdcce..f5dd19552b7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -255,14 +255,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit final boolean afterKeyguardGone = isActivityIntent && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); - final boolean wasOccluded = mStatusBar.isOccluded(); + final boolean animate = mStatusBar.shouldAnimateLaunch(isActivityIntent); boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); ActivityStarter.OnDismissAction postKeyguardAction = () -> handleNotificationClickAfterKeyguardDismissed( entry, row, controller, intent, - isActivityIntent, wasOccluded, showOverLockscreen); + isActivityIntent, animate, showOverLockscreen); if (showOverLockscreen) { mIsCollapsingToShowActivityOverLockscreen = true; postKeyguardAction.onDismiss(); @@ -278,7 +278,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit RemoteInputController controller, PendingIntent intent, boolean isActivityIntent, - boolean wasOccluded, + boolean animate, boolean showOverLockscreen) { mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey()); @@ -293,7 +293,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed( entry, row, controller, intent, - isActivityIntent, wasOccluded); + isActivityIntent, animate); if (showOverLockscreen) { mShadeController.addPostCollapseAction(runnable); @@ -314,7 +314,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit RemoteInputController controller, PendingIntent intent, boolean isActivityIntent, - boolean wasOccluded) { + boolean animate) { String notificationKey = entry.getKey(); mLogger.logHandleClickAfterPanelCollapsed(notificationKey); @@ -360,8 +360,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit removeHUN(row); expandBubbleStackOnMainThread(entry); } else { - startNotificationIntent( - intent, fillInIntent, entry, row, wasOccluded, isActivityIntent); + startNotificationIntent(intent, fillInIntent, entry, row, animate, isActivityIntent); } if (isActivityIntent || canBubble) { mAssistManagerLazy.get().hideAssist(); @@ -426,7 +425,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Intent fillInIntent, NotificationEntry entry, ExpandableNotificationRow row, - boolean wasOccluded, + boolean animate, boolean isActivityIntent) { mLogger.logStartNotificationIntent(entry.getKey(), intent); try { @@ -436,8 +435,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit isActivityIntent); mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController, - !wasOccluded && mStatusBar.areLaunchAnimationsEnabled(), - intent.getCreatorPackage(), (adapter) -> { + animate, intent.getCreatorPackage(), (adapter) -> { long eventTime = row.getAndResetLastActionUpTime(); Bundle options = eventTime > 0 ? getActivityOptions( @@ -460,6 +458,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public void startNotificationGutsIntent(final Intent intent, final int appUid, ExpandableNotificationRow row) { + boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */); mActivityStarter.dismissKeyguardThenExecute(() -> { AsyncTask.execute(() -> { ActivityLaunchAnimator.Controller animationController = @@ -468,8 +467,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBar, true /* isActivityIntent */); mActivityLaunchAnimator.startIntentWithAnimation( - animationController, mStatusBar.areLaunchAnimationsEnabled(), - intent.getPackage(), + animationController, animate, intent.getPackage(), (adapter) -> TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions( @@ -483,6 +481,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public void startHistoryIntent(View view, boolean showHistory) { + boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */); mActivityStarter.dismissKeyguardThenExecute(() -> { AsyncTask.execute(() -> { Intent intent = showHistory ? new Intent( @@ -499,8 +498,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit ActivityLaunchAnimator.Controller.fromView(view), mStatusBar, true /* isActivityIntent */); - mActivityLaunchAnimator.startIntentWithAnimation(animationController, - mStatusBar.areLaunchAnimationsEnabled(), intent.getPackage(), + mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, + intent.getPackage(), (adapter) -> tsb.startActivities( getActivityOptions(mStatusBar.getDisplayId(), adapter), UserHandle.CURRENT)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt index f2ef5c38e9b9..9a0ed9897e83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -8,6 +8,7 @@ import android.os.Looper import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.IRemoteAnimationFinishedCallback +import android.view.IRemoteAnimationRunner import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget import android.view.SurfaceControl @@ -15,6 +16,7 @@ import android.view.ViewGroup import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any import junit.framework.Assert.assertFalse import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertNull @@ -27,6 +29,7 @@ import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.Mockito.never +import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Spy import org.mockito.junit.MockitoJUnit @@ -36,14 +39,16 @@ import kotlin.concurrent.thread @RunWith(AndroidTestingRunner::class) @RunWithLooper class ActivityLaunchAnimatorTest : SysuiTestCase() { - private val activityLaunchAnimator = ActivityLaunchAnimator(mContext) private val launchContainer = LinearLayout(mContext) + private val keyguardHandler = TestLaunchAnimatorKeyguardHandler(isOnKeyguard = false) @Spy private val controller = TestLaunchAnimatorController(launchContainer) @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback + private val activityLaunchAnimator = ActivityLaunchAnimator(keyguardHandler, mContext) @get:Rule val rule = MockitoJUnit.rule() private fun startIntentWithAnimation( + animator: ActivityLaunchAnimator = this.activityLaunchAnimator, controller: ActivityLaunchAnimator.Controller? = this.controller, animate: Boolean = true, intentStarter: (RemoteAnimationAdapter?) -> Int @@ -51,7 +56,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { // We start in a new thread so that we can ensure that the callbacks are called in the main // thread. thread { - activityLaunchAnimator.startIntentWithAnimation( + animator.startIntentWithAnimation( controller = controller, animate = animate, intentStarter = intentStarter @@ -101,6 +106,27 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { } @Test + fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() { + val keyguardHandler = spy(TestLaunchAnimatorKeyguardHandler(isOnKeyguard = true)) + val animator = ActivityLaunchAnimator(keyguardHandler, context) + + val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java) + var animationAdapter: RemoteAnimationAdapter? = null + + startIntentWithAnimation(animator) { adapter -> + animationAdapter = adapter + ActivityManager.START_DELIVERED_TO_TOP + } + + waitForIdleSync() + verify(controller).onIntentStarted(willAnimateCaptor.capture()) + verify(keyguardHandler).hideKeyguardWithAnimation(any()) + + assertTrue(willAnimateCaptor.value) + assertNull(animationAdapter) + } + + @Test fun doesNotAnimateIfAnimateIsFalse() { val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java) startIntentWithAnimation(animate = false) { ActivityManager.START_SUCCESS } @@ -149,6 +175,16 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { } } +private class TestLaunchAnimatorKeyguardHandler( + private val isOnKeyguard: Boolean +) : ActivityLaunchAnimator.KeyguardHandler { + override fun isOnKeyguard(): Boolean = isOnKeyguard + + override fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) { + // Do nothing. + } +} + /** * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called * outside of the main thread. |