Merge "Animate app launches from lockscreen" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2c52d52f6d
@@ -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
|
* A class that allows activities to be started in a seamless way from a view that is transforming
|
||||||
* nicely into the starting window.
|
* nicely into the starting window.
|
||||||
*/
|
*/
|
||||||
class ActivityLaunchAnimator(context: Context) {
|
class ActivityLaunchAnimator(
|
||||||
|
private val keyguardHandler: KeyguardHandler,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
private val TAG = this::class.java.simpleName
|
private val TAG = this::class.java.simpleName
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -104,15 +107,22 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
|
|
||||||
Log.d(TAG, "Starting intent with a launch animation")
|
Log.d(TAG, "Starting intent with a launch animation")
|
||||||
val runner = Runner(controller)
|
val runner = Runner(controller)
|
||||||
val animationAdapter = RemoteAnimationAdapter(
|
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,
|
runner,
|
||||||
ANIMATION_DURATION,
|
ANIMATION_DURATION,
|
||||||
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
|
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
// Register the remote animation for the given package to also animate trampoline
|
// Register the remote animation for the given package to also animate trampoline
|
||||||
// activity launches.
|
// activity launches.
|
||||||
if (packageName != null) {
|
if (packageName != null && animationAdapter != null) {
|
||||||
try {
|
try {
|
||||||
ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(
|
ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(
|
||||||
packageName, animationAdapter)
|
packageName, animationAdapter)
|
||||||
@@ -122,20 +132,30 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val launchResult = intentStarter(animationAdapter)
|
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)
|
controller.callOnIntentStartedOnMainThread(willAnimate)
|
||||||
|
|
||||||
// If we expect an animation, post a timeout to cancel it in case the remote animation is
|
// If we expect an animation, post a timeout to cancel it in case the remote animation is
|
||||||
// never started.
|
// never started.
|
||||||
if (willAnimate) {
|
if (willAnimate) {
|
||||||
runner.postTimeout()
|
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()) {
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||||
this.launchContainer.context.mainExecutor.execute {
|
this.launchContainer.context.mainExecutor.execute {
|
||||||
this.onIntentStarted(willAnimate)
|
this.onIntentStarted(willAnimate)
|
||||||
@@ -179,6 +199,14 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int
|
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.
|
* A controller that takes care of applying the animation to an expanding view.
|
||||||
*
|
*
|
||||||
@@ -337,17 +365,17 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
|
|
||||||
override fun onAnimationStart(
|
override fun onAnimationStart(
|
||||||
@WindowManager.TransitionOldType transit: Int,
|
@WindowManager.TransitionOldType transit: Int,
|
||||||
remoteAnimationTargets: Array<out RemoteAnimationTarget>,
|
apps: Array<out RemoteAnimationTarget>?,
|
||||||
remoteAnimationWallpaperTargets: Array<out RemoteAnimationTarget>,
|
wallpapers: Array<out RemoteAnimationTarget>?,
|
||||||
remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
|
nonApps: Array<out RemoteAnimationTarget>?,
|
||||||
iRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback
|
iCallback: IRemoteAnimationFinishedCallback?
|
||||||
) {
|
) {
|
||||||
removeTimeout()
|
removeTimeout()
|
||||||
|
|
||||||
// The animation was started too late and we already notified the controller that it
|
// The animation was started too late and we already notified the controller that it
|
||||||
// timed out.
|
// timed out.
|
||||||
if (timedOut) {
|
if (timedOut) {
|
||||||
invokeCallback(iRemoteAnimationFinishedCallback)
|
iCallback?.invoke()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,30 +386,29 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.mainExecutor.execute {
|
context.mainExecutor.execute {
|
||||||
startAnimation(remoteAnimationTargets, remoteAnimationNonAppTargets,
|
startAnimation(apps, nonApps, iCallback)
|
||||||
iRemoteAnimationFinishedCallback)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startAnimation(
|
private fun startAnimation(
|
||||||
remoteAnimationTargets: Array<out RemoteAnimationTarget>,
|
apps: Array<out RemoteAnimationTarget>?,
|
||||||
remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
|
nonApps: Array<out RemoteAnimationTarget>?,
|
||||||
iCallback: IRemoteAnimationFinishedCallback
|
iCallback: IRemoteAnimationFinishedCallback?
|
||||||
) {
|
) {
|
||||||
Log.d(TAG, "Remote animation started")
|
Log.d(TAG, "Remote animation started")
|
||||||
val window = remoteAnimationTargets.firstOrNull {
|
val window = apps?.firstOrNull {
|
||||||
it.mode == RemoteAnimationTarget.MODE_OPENING
|
it.mode == RemoteAnimationTarget.MODE_OPENING
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window == null) {
|
if (window == null) {
|
||||||
Log.d(TAG, "Aborting the animation as no window is opening")
|
Log.d(TAG, "Aborting the animation as no window is opening")
|
||||||
removeTimeout()
|
removeTimeout()
|
||||||
invokeCallback(iCallback)
|
iCallback?.invoke()
|
||||||
controller.onLaunchAnimationCancelled()
|
controller.onLaunchAnimationCancelled()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val navigationBar = remoteAnimationNonAppTargets.firstOrNull {
|
val navigationBar = nonApps?.firstOrNull {
|
||||||
it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
|
it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,7 +466,7 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
|
|
||||||
override fun onAnimationEnd(animation: Animator?) {
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
Log.d(TAG, "Animation ended")
|
Log.d(TAG, "Animation ended")
|
||||||
invokeCallback(iCallback)
|
iCallback?.invoke()
|
||||||
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
|
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
|
// The scale will also be applied to the corner radius, so we divide by the scale to
|
||||||
// keep the original radius.
|
// keep the original radius. We use the max of (topCornerRadius, bottomCornerRadius) to
|
||||||
val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius) / scale
|
// 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)
|
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
|
||||||
.withAlpha(1f)
|
.withAlpha(1f)
|
||||||
.withMatrix(matrix)
|
.withMatrix(matrix)
|
||||||
@@ -585,9 +615,9 @@ class ActivityLaunchAnimator(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun invokeCallback(iCallback: IRemoteAnimationFinishedCallback) {
|
private fun IRemoteAnimationFinishedCallback.invoke() {
|
||||||
try {
|
try {
|
||||||
iCallback.onAnimationFinished()
|
onAnimationFinished()
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class KeyguardService extends Service {
|
|||||||
/**
|
/**
|
||||||
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
|
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
|
||||||
*/
|
*/
|
||||||
static boolean sEnableRemoteKeyguardAnimation =
|
public static boolean sEnableRemoteKeyguardAnimation =
|
||||||
SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
|
SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
|
||||||
|
|
||||||
private final KeyguardViewMediator mKeyguardViewMediator;
|
private final KeyguardViewMediator mKeyguardViewMediator;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import android.media.SoundPool;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.DeadObjectException;
|
import android.os.DeadObjectException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
@@ -71,6 +72,7 @@ import android.util.Slog;
|
|||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.view.IRemoteAnimationFinishedCallback;
|
import android.view.IRemoteAnimationFinishedCallback;
|
||||||
|
import android.view.IRemoteAnimationRunner;
|
||||||
import android.view.RemoteAnimationTarget;
|
import android.view.RemoteAnimationTarget;
|
||||||
import android.view.SyncRtSurfaceTransactionApplier;
|
import android.view.SyncRtSurfaceTransactionApplier;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -426,6 +428,11 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
*/
|
*/
|
||||||
private IRemoteAnimationFinishedCallback mSurfaceBehindRemoteAnimationFinishedCallback;
|
private IRemoteAnimationFinishedCallback mSurfaceBehindRemoteAnimationFinishedCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation runner to use for the next exit animation.
|
||||||
|
*/
|
||||||
|
private IRemoteAnimationRunner mKeyguardExitAnimationRunner;
|
||||||
|
|
||||||
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
|
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
|
||||||
new DeviceConfig.OnPropertiesChangedListener() {
|
new DeviceConfig.OnPropertiesChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -1605,6 +1612,16 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
Trace.endSection();
|
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() {
|
public boolean isSecure() {
|
||||||
return isSecure(KeyguardUpdateMonitor.getCurrentUser());
|
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
|
// TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
|
||||||
// still completes and makes the screen blank.
|
// still completes and makes the screen blank.
|
||||||
if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
|
if (DEBUG) Log.d(TAG, "Split system user, quit unlocking.");
|
||||||
|
mKeyguardExitAnimationRunner = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mHiding = true;
|
mHiding = true;
|
||||||
@@ -2103,9 +2121,37 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
playSounds(false);
|
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,
|
// When remaining on the shade, there's no need to do a fancy remote animation,
|
||||||
// it will dismiss the panel in that case.
|
// it will dismiss the panel in that case.
|
||||||
if (KeyguardService.sEnableRemoteKeyguardAnimation
|
} else if (KeyguardService.sEnableRemoteKeyguardAnimation
|
||||||
&& !mStatusBarStateController.leaveOpenOnKeyguardHide()
|
&& !mStatusBarStateController.leaveOpenOnKeyguardHide()
|
||||||
&& apps != null && apps.length > 0) {
|
&& apps != null && apps.length > 0) {
|
||||||
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
|
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
|
||||||
@@ -2115,9 +2161,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
|
mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
|
||||||
apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
|
apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
|
||||||
} else {
|
} else {
|
||||||
setShowingLocked(false);
|
|
||||||
mWakeAndUnlocking = false;
|
|
||||||
mDismissCallbackRegistry.notifyDismissSucceeded();
|
|
||||||
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
|
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
|
||||||
|
|
||||||
// TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
|
// TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
|
||||||
@@ -2165,16 +2208,24 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
});
|
});
|
||||||
anim.start();
|
anim.start();
|
||||||
});
|
});
|
||||||
resetKeyguardDonePendingLocked();
|
|
||||||
mHideAnimationRun = false;
|
onKeyguardExitFinished();
|
||||||
adjustStatusBarLocked();
|
|
||||||
sendUserPresentBroadcast();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Trace.endSection();
|
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
|
* 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).
|
* 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.
|
// Block the panel from expanding, in case we were doing a swipe to dismiss gesture.
|
||||||
mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
|
mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
|
||||||
final boolean wasShowing = mShowing;
|
final boolean wasShowing = mShowing;
|
||||||
setShowingLocked(false);
|
onKeyguardExitFinished();
|
||||||
|
|
||||||
mWakeAndUnlocking = false;
|
|
||||||
mDismissCallbackRegistry.notifyDismissSucceeded();
|
|
||||||
|
|
||||||
if (mKeyguardStateController.isDismissingFromSwipe() || !wasShowing) {
|
if (mKeyguardStateController.isDismissingFromSwipe() || !wasShowing) {
|
||||||
mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
|
mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
finishSurfaceBehindRemoteAnimation();
|
finishSurfaceBehindRemoteAnimation();
|
||||||
|
|
||||||
resetKeyguardDonePendingLocked();
|
|
||||||
mHideAnimationRun = false;
|
|
||||||
adjustStatusBarLocked();
|
|
||||||
sendUserPresentBroadcast();
|
|
||||||
mSurfaceBehindRemoteAnimationRequested = false;
|
mSurfaceBehindRemoteAnimationRequested = false;
|
||||||
mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
|
mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
|||||||
return super.onInterceptTouchEvent(ev);
|
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. */
|
/** Sets the last action up time this view was touched. */
|
||||||
void setLastActionUpTime(long eventTime) {
|
void setLastActionUpTime(long eventTime) {
|
||||||
mLastActionUpTime = eventTime;
|
mLastActionUpTime = eventTime;
|
||||||
|
|||||||
@@ -118,7 +118,11 @@ public class ActivatableNotificationViewController
|
|||||||
|
|
||||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||||
// If this is a false tap, capture the even so it doesn't result in a click.
|
// 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2290,6 +2290,23 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
setRippleAllowed(allowed);
|
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
|
@Override
|
||||||
public int getIntrinsicHeight() {
|
public int getIntrinsicHeight() {
|
||||||
if (isUserLocked()) {
|
if (isUserLocked()) {
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ import android.util.Log;
|
|||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
import android.view.IRemoteAnimationRunner;
|
||||||
import android.view.IWindowManager;
|
import android.view.IWindowManager;
|
||||||
import android.view.InsetsState.InternalInsetsType;
|
import android.view.InsetsState.InternalInsetsType;
|
||||||
import android.view.KeyEvent;
|
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.ExtensionFragmentListener;
|
||||||
import com.android.systemui.fragments.FragmentHostManager;
|
import com.android.systemui.fragments.FragmentHostManager;
|
||||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||||
|
import com.android.systemui.keyguard.KeyguardService;
|
||||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
import com.android.systemui.keyguard.KeyguardViewMediator;
|
||||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||||
@@ -267,7 +269,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
OnHeadsUpChangedListener, CommandQueue.Callbacks,
|
OnHeadsUpChangedListener, CommandQueue.Callbacks,
|
||||||
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
|
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
|
||||||
StatusBarStateController.StateListener,
|
StatusBarStateController.StateListener,
|
||||||
LifecycleOwner, BatteryController.BatteryStateChangeCallback {
|
LifecycleOwner, BatteryController.BatteryStateChangeCallback,
|
||||||
|
ActivityLaunchAnimator.KeyguardHandler {
|
||||||
public static final boolean MULTIUSER_DEBUG = false;
|
public static final boolean MULTIUSER_DEBUG = false;
|
||||||
|
|
||||||
protected static final int MSG_HIDE_RECENT_APPS = 1020;
|
protected static final int MSG_HIDE_RECENT_APPS = 1020;
|
||||||
@@ -1413,7 +1416,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
|
|
||||||
private void setUpPresenter() {
|
private void setUpPresenter() {
|
||||||
// Set up the initial notification state.
|
// Set up the initial notification state.
|
||||||
mActivityLaunchAnimator = new ActivityLaunchAnimator(mContext);
|
mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext);
|
||||||
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
|
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
|
||||||
mNotificationShadeWindowViewController,
|
mNotificationShadeWindowViewController,
|
||||||
mStackScrollerController.getNotificationListContainer(),
|
mStackScrollerController.getNotificationListContainer(),
|
||||||
@@ -2072,10 +2075,45 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether we should animate an activity launch. */
|
/**
|
||||||
public boolean areLaunchAnimationsEnabled() {
|
* Whether we should animate an activity launch.
|
||||||
// TODO(b/184121838): Support lock screen launch animations.
|
*
|
||||||
return mState == StatusBarState.SHADE && !isOccluded();
|
* 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() {
|
public boolean isDeviceInVrMode() {
|
||||||
@@ -2796,8 +2834,9 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
|
final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
|
||||||
intent, mLockscreenUserManager.getCurrentUserId());
|
intent, mLockscreenUserManager.getCurrentUserId());
|
||||||
|
|
||||||
ActivityLaunchAnimator.Controller animController = wrapAnimationController(
|
ActivityLaunchAnimator.Controller animController =
|
||||||
animationController, dismissShade);
|
shouldAnimateLaunch(true /* isActivityIntent */) ? wrapAnimationController(
|
||||||
|
animationController, dismissShade) : null;
|
||||||
|
|
||||||
// If we animate, we will dismiss the shade only once the animation is done. This is taken
|
// If we animate, we will dismiss the shade only once the animation is done. This is taken
|
||||||
// care of by the StatusBarLaunchAnimationController.
|
// care of by the StatusBarLaunchAnimationController.
|
||||||
@@ -2811,7 +2850,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
int[] result = new int[]{ActivityManager.START_CANCELED};
|
int[] result = new int[]{ActivityManager.START_CANCELED};
|
||||||
|
|
||||||
mActivityLaunchAnimator.startIntentWithAnimation(animController,
|
mActivityLaunchAnimator.startIntentWithAnimation(animController,
|
||||||
areLaunchAnimationsEnabled(), intent.getPackage(), (adapter) -> {
|
true /* animate */, intent.getPackage(), (adapter) -> {
|
||||||
ActivityOptions options = new ActivityOptions(
|
ActivityOptions options = new ActivityOptions(
|
||||||
getActivityOptions(mDisplayId, adapter));
|
getActivityOptions(mDisplayId, adapter));
|
||||||
options.setDisallowEnterPictureInPictureWhileLaunching(
|
options.setDisallowEnterPictureInPictureWhileLaunching(
|
||||||
@@ -4606,6 +4645,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
mLockscreenUserManager.getCurrentUserId());
|
mLockscreenUserManager.getCurrentUserId());
|
||||||
|
|
||||||
boolean collapse = animationController == null;
|
boolean collapse = animationController == null;
|
||||||
|
boolean animate = shouldAnimateLaunch(intent.isActivity());
|
||||||
executeActionDismissingKeyguard(() -> {
|
executeActionDismissingKeyguard(() -> {
|
||||||
try {
|
try {
|
||||||
// We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
|
// 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;
|
animationController, this, intent.isActivity()) : null;
|
||||||
|
|
||||||
mActivityLaunchAnimator.startPendingIntentWithAnimation(
|
mActivityLaunchAnimator.startPendingIntentWithAnimation(
|
||||||
controller, areLaunchAnimationsEnabled(), intent.getCreatorPackage(),
|
controller, animate, intent.getCreatorPackage(),
|
||||||
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
|
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
|
||||||
null, getActivityOptions(mDisplayId, animationAdapter)));
|
null, getActivityOptions(mDisplayId, animationAdapter)));
|
||||||
} catch (PendingIntent.CanceledException e) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
|
|||||||
@@ -255,14 +255,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
final boolean afterKeyguardGone = isActivityIntent
|
final boolean afterKeyguardGone = isActivityIntent
|
||||||
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
|
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
|
||||||
mLockscreenUserManager.getCurrentUserId());
|
mLockscreenUserManager.getCurrentUserId());
|
||||||
final boolean wasOccluded = mStatusBar.isOccluded();
|
final boolean animate = mStatusBar.shouldAnimateLaunch(isActivityIntent);
|
||||||
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
|
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
|
||||||
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
|
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
|
||||||
mLockscreenUserManager.getCurrentUserId());
|
mLockscreenUserManager.getCurrentUserId());
|
||||||
ActivityStarter.OnDismissAction postKeyguardAction =
|
ActivityStarter.OnDismissAction postKeyguardAction =
|
||||||
() -> handleNotificationClickAfterKeyguardDismissed(
|
() -> handleNotificationClickAfterKeyguardDismissed(
|
||||||
entry, row, controller, intent,
|
entry, row, controller, intent,
|
||||||
isActivityIntent, wasOccluded, showOverLockscreen);
|
isActivityIntent, animate, showOverLockscreen);
|
||||||
if (showOverLockscreen) {
|
if (showOverLockscreen) {
|
||||||
mIsCollapsingToShowActivityOverLockscreen = true;
|
mIsCollapsingToShowActivityOverLockscreen = true;
|
||||||
postKeyguardAction.onDismiss();
|
postKeyguardAction.onDismiss();
|
||||||
@@ -278,7 +278,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
RemoteInputController controller,
|
RemoteInputController controller,
|
||||||
PendingIntent intent,
|
PendingIntent intent,
|
||||||
boolean isActivityIntent,
|
boolean isActivityIntent,
|
||||||
boolean wasOccluded,
|
boolean animate,
|
||||||
boolean showOverLockscreen) {
|
boolean showOverLockscreen) {
|
||||||
mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey());
|
mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey());
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
|
|
||||||
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
|
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
|
||||||
entry, row, controller, intent,
|
entry, row, controller, intent,
|
||||||
isActivityIntent, wasOccluded);
|
isActivityIntent, animate);
|
||||||
|
|
||||||
if (showOverLockscreen) {
|
if (showOverLockscreen) {
|
||||||
mShadeController.addPostCollapseAction(runnable);
|
mShadeController.addPostCollapseAction(runnable);
|
||||||
@@ -314,7 +314,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
RemoteInputController controller,
|
RemoteInputController controller,
|
||||||
PendingIntent intent,
|
PendingIntent intent,
|
||||||
boolean isActivityIntent,
|
boolean isActivityIntent,
|
||||||
boolean wasOccluded) {
|
boolean animate) {
|
||||||
String notificationKey = entry.getKey();
|
String notificationKey = entry.getKey();
|
||||||
mLogger.logHandleClickAfterPanelCollapsed(notificationKey);
|
mLogger.logHandleClickAfterPanelCollapsed(notificationKey);
|
||||||
|
|
||||||
@@ -360,8 +360,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
removeHUN(row);
|
removeHUN(row);
|
||||||
expandBubbleStackOnMainThread(entry);
|
expandBubbleStackOnMainThread(entry);
|
||||||
} else {
|
} else {
|
||||||
startNotificationIntent(
|
startNotificationIntent(intent, fillInIntent, entry, row, animate, isActivityIntent);
|
||||||
intent, fillInIntent, entry, row, wasOccluded, isActivityIntent);
|
|
||||||
}
|
}
|
||||||
if (isActivityIntent || canBubble) {
|
if (isActivityIntent || canBubble) {
|
||||||
mAssistManagerLazy.get().hideAssist();
|
mAssistManagerLazy.get().hideAssist();
|
||||||
@@ -426,7 +425,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
Intent fillInIntent,
|
Intent fillInIntent,
|
||||||
NotificationEntry entry,
|
NotificationEntry entry,
|
||||||
ExpandableNotificationRow row,
|
ExpandableNotificationRow row,
|
||||||
boolean wasOccluded,
|
boolean animate,
|
||||||
boolean isActivityIntent) {
|
boolean isActivityIntent) {
|
||||||
mLogger.logStartNotificationIntent(entry.getKey(), intent);
|
mLogger.logStartNotificationIntent(entry.getKey(), intent);
|
||||||
try {
|
try {
|
||||||
@@ -436,8 +435,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
isActivityIntent);
|
isActivityIntent);
|
||||||
|
|
||||||
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
|
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
|
||||||
!wasOccluded && mStatusBar.areLaunchAnimationsEnabled(),
|
animate, intent.getCreatorPackage(), (adapter) -> {
|
||||||
intent.getCreatorPackage(), (adapter) -> {
|
|
||||||
long eventTime = row.getAndResetLastActionUpTime();
|
long eventTime = row.getAndResetLastActionUpTime();
|
||||||
Bundle options = eventTime > 0
|
Bundle options = eventTime > 0
|
||||||
? getActivityOptions(
|
? getActivityOptions(
|
||||||
@@ -460,6 +458,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
@Override
|
@Override
|
||||||
public void startNotificationGutsIntent(final Intent intent, final int appUid,
|
public void startNotificationGutsIntent(final Intent intent, final int appUid,
|
||||||
ExpandableNotificationRow row) {
|
ExpandableNotificationRow row) {
|
||||||
|
boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
|
||||||
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
||||||
AsyncTask.execute(() -> {
|
AsyncTask.execute(() -> {
|
||||||
ActivityLaunchAnimator.Controller animationController =
|
ActivityLaunchAnimator.Controller animationController =
|
||||||
@@ -468,8 +467,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
mStatusBar, true /* isActivityIntent */);
|
mStatusBar, true /* isActivityIntent */);
|
||||||
|
|
||||||
mActivityLaunchAnimator.startIntentWithAnimation(
|
mActivityLaunchAnimator.startIntentWithAnimation(
|
||||||
animationController, mStatusBar.areLaunchAnimationsEnabled(),
|
animationController, animate, intent.getPackage(),
|
||||||
intent.getPackage(),
|
|
||||||
(adapter) -> TaskStackBuilder.create(mContext)
|
(adapter) -> TaskStackBuilder.create(mContext)
|
||||||
.addNextIntentWithParentStack(intent)
|
.addNextIntentWithParentStack(intent)
|
||||||
.startActivities(getActivityOptions(
|
.startActivities(getActivityOptions(
|
||||||
@@ -483,6 +481,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startHistoryIntent(View view, boolean showHistory) {
|
public void startHistoryIntent(View view, boolean showHistory) {
|
||||||
|
boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
|
||||||
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
||||||
AsyncTask.execute(() -> {
|
AsyncTask.execute(() -> {
|
||||||
Intent intent = showHistory ? new Intent(
|
Intent intent = showHistory ? new Intent(
|
||||||
@@ -499,8 +498,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
|
ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
|
||||||
true /* isActivityIntent */);
|
true /* isActivityIntent */);
|
||||||
|
|
||||||
mActivityLaunchAnimator.startIntentWithAnimation(animationController,
|
mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
|
||||||
mStatusBar.areLaunchAnimationsEnabled(), intent.getPackage(),
|
intent.getPackage(),
|
||||||
(adapter) -> tsb.startActivities(
|
(adapter) -> tsb.startActivities(
|
||||||
getActivityOptions(mStatusBar.getDisplayId(), adapter),
|
getActivityOptions(mStatusBar.getDisplayId(), adapter),
|
||||||
UserHandle.CURRENT));
|
UserHandle.CURRENT));
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.os.Looper
|
|||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.testing.TestableLooper.RunWithLooper
|
import android.testing.TestableLooper.RunWithLooper
|
||||||
import android.view.IRemoteAnimationFinishedCallback
|
import android.view.IRemoteAnimationFinishedCallback
|
||||||
|
import android.view.IRemoteAnimationRunner
|
||||||
import android.view.RemoteAnimationAdapter
|
import android.view.RemoteAnimationAdapter
|
||||||
import android.view.RemoteAnimationTarget
|
import android.view.RemoteAnimationTarget
|
||||||
import android.view.SurfaceControl
|
import android.view.SurfaceControl
|
||||||
@@ -15,6 +16,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
import junit.framework.Assert.assertFalse
|
import junit.framework.Assert.assertFalse
|
||||||
import junit.framework.Assert.assertNotNull
|
import junit.framework.Assert.assertNotNull
|
||||||
import junit.framework.Assert.assertNull
|
import junit.framework.Assert.assertNull
|
||||||
@@ -27,6 +29,7 @@ import org.mockito.ArgumentCaptor
|
|||||||
import org.mockito.ArgumentMatchers.anyBoolean
|
import org.mockito.ArgumentMatchers.anyBoolean
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.Spy
|
import org.mockito.Spy
|
||||||
import org.mockito.junit.MockitoJUnit
|
import org.mockito.junit.MockitoJUnit
|
||||||
@@ -36,14 +39,16 @@ import kotlin.concurrent.thread
|
|||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@RunWithLooper
|
@RunWithLooper
|
||||||
class ActivityLaunchAnimatorTest : SysuiTestCase() {
|
class ActivityLaunchAnimatorTest : SysuiTestCase() {
|
||||||
private val activityLaunchAnimator = ActivityLaunchAnimator(mContext)
|
|
||||||
private val launchContainer = LinearLayout(mContext)
|
private val launchContainer = LinearLayout(mContext)
|
||||||
|
private val keyguardHandler = TestLaunchAnimatorKeyguardHandler(isOnKeyguard = false)
|
||||||
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
|
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
|
||||||
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
|
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
|
||||||
|
private val activityLaunchAnimator = ActivityLaunchAnimator(keyguardHandler, mContext)
|
||||||
|
|
||||||
@get:Rule val rule = MockitoJUnit.rule()
|
@get:Rule val rule = MockitoJUnit.rule()
|
||||||
|
|
||||||
private fun startIntentWithAnimation(
|
private fun startIntentWithAnimation(
|
||||||
|
animator: ActivityLaunchAnimator = this.activityLaunchAnimator,
|
||||||
controller: ActivityLaunchAnimator.Controller? = this.controller,
|
controller: ActivityLaunchAnimator.Controller? = this.controller,
|
||||||
animate: Boolean = true,
|
animate: Boolean = true,
|
||||||
intentStarter: (RemoteAnimationAdapter?) -> Int
|
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
|
// We start in a new thread so that we can ensure that the callbacks are called in the main
|
||||||
// thread.
|
// thread.
|
||||||
thread {
|
thread {
|
||||||
activityLaunchAnimator.startIntentWithAnimation(
|
animator.startIntentWithAnimation(
|
||||||
controller = controller,
|
controller = controller,
|
||||||
animate = animate,
|
animate = animate,
|
||||||
intentStarter = intentStarter
|
intentStarter = intentStarter
|
||||||
@@ -100,6 +105,27 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
|
|||||||
assertFalse(willAnimateCaptor.value)
|
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
|
@Test
|
||||||
fun doesNotAnimateIfAnimateIsFalse() {
|
fun doesNotAnimateIfAnimateIsFalse() {
|
||||||
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
|
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
|
* A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
|
||||||
* outside of the main thread.
|
* outside of the main thread.
|
||||||
|
|||||||
Reference in New Issue
Block a user