Merge "Animate app launches from lockscreen" into sc-dev am: 2c52d52f6d am: fabc5f4851
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14411116 Change-Id: I47e2656e0346c1e63fd8daa6b149877f5d74c300
This commit is contained in:
committed by
Automerger Merge Worker
commit
07b59b2441
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2290,6 +2290,23 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
setRippleAllowed(allowed);
|
||||
}
|
||||
|
||||
@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()) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
@@ -100,6 +105,27 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
|
||||
assertFalse(willAnimateCaptor.value)
|
||||
}
|
||||
|
||||
@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)
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user