Merge "Register the remote animation before starting an intent." into sc-dev am: 8dab740d09 am: fce6b1c050

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14663313

Change-Id: If0904e440fa1fbc0db9a7dbd33d5d265277febb8
This commit is contained in:
Jordan Demeulenaere
2021-05-25 08:58:08 +00:00
committed by Automerger Merge Worker
5 changed files with 50 additions and 11 deletions

View File

@@ -4,12 +4,14 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.app.ActivityManager import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.graphics.Matrix import android.graphics.Matrix
import android.graphics.Rect import android.graphics.Rect
import android.os.Looper import android.os.Looper
import android.os.RemoteException import android.os.RemoteException
import android.util.Log
import android.util.MathUtils import android.util.MathUtils
import android.view.IRemoteAnimationFinishedCallback import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner import android.view.IRemoteAnimationRunner
@@ -30,6 +32,8 @@ import kotlin.math.roundToInt
* nicely into the starting window. * nicely into the starting window.
*/ */
class ActivityLaunchAnimator(context: Context) { class ActivityLaunchAnimator(context: Context) {
private val TAG = this::class.java.simpleName
companion object { companion object {
const val ANIMATION_DURATION = 500L const val ANIMATION_DURATION = 500L
const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L
@@ -78,29 +82,49 @@ class ActivityLaunchAnimator(context: Context) {
* If [controller] is null or [animate] is false, then the intent will be started and no * If [controller] is null or [animate] is false, then the intent will be started and no
* animation will run. * animation will run.
* *
* If possible, you should pass the [packageName] of the intent that will be started so that
* trampoline activity launches will also be animated.
*
* This method will throw any exception thrown by [intentStarter]. * This method will throw any exception thrown by [intentStarter].
*/ */
@JvmOverloads @JvmOverloads
inline fun startIntentWithAnimation( fun startIntentWithAnimation(
controller: Controller?, controller: Controller?,
animate: Boolean = true, animate: Boolean = true,
packageName: String? = null,
intentStarter: (RemoteAnimationAdapter?) -> Int intentStarter: (RemoteAnimationAdapter?) -> Int
) { ) {
if (controller == null || !animate) { if (controller == null || !animate) {
Log.d(TAG, "Starting intent with no animation")
intentStarter(null) intentStarter(null)
controller?.callOnIntentStartedOnMainThread(willAnimate = false) controller?.callOnIntentStartedOnMainThread(willAnimate = false)
return return
} }
Log.d(TAG, "Starting intent with a launch animation")
val runner = Runner(controller) val runner = Runner(controller)
val animationAdapter = RemoteAnimationAdapter( val animationAdapter = RemoteAnimationAdapter(
runner, runner,
ANIMATION_DURATION, ANIMATION_DURATION,
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */ ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
) )
// Register the remote animation for the given package to also animate trampoline
// activity launches.
if (packageName != null) {
try {
ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(
packageName, animationAdapter)
} catch (e: RemoteException) {
Log.w(TAG, "Unable to register the remote animation", e)
}
}
val launchResult = intentStarter(animationAdapter) val launchResult = intentStarter(animationAdapter)
val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT || val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
launchResult == ActivityManager.START_SUCCESS launchResult == ActivityManager.START_SUCCESS
Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate")
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
@@ -110,7 +134,6 @@ class ActivityLaunchAnimator(context: Context) {
} }
} }
@PublishedApi
internal fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) { internal 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 {
@@ -125,15 +148,21 @@ class ActivityLaunchAnimator(context: Context) {
* Same as [startIntentWithAnimation] but allows [intentStarter] to throw a * Same as [startIntentWithAnimation] but allows [intentStarter] to throw a
* [PendingIntent.CanceledException] which must then be handled by the caller. This is useful * [PendingIntent.CanceledException] which must then be handled by the caller. This is useful
* for Java caller starting a [PendingIntent]. * for Java caller starting a [PendingIntent].
*
* If possible, you should pass the [packageName] of the intent that will be started so that
* trampoline activity launches will also be animated.
*/ */
@Throws(PendingIntent.CanceledException::class) @Throws(PendingIntent.CanceledException::class)
@JvmOverloads @JvmOverloads
fun startPendingIntentWithAnimation( fun startPendingIntentWithAnimation(
controller: Controller?, controller: Controller?,
animate: Boolean = true, animate: Boolean = true,
packageName: String? = null,
intentStarter: PendingIntentStarter intentStarter: PendingIntentStarter
) { ) {
startIntentWithAnimation(controller, animate) { intentStarter.startPendingIntent(it) } startIntentWithAnimation(controller, animate, packageName) {
intentStarter.startPendingIntent(it)
}
} }
/** Create a new animation [Runner] controlled by [controller]. */ /** Create a new animation [Runner] controlled by [controller]. */
@@ -278,7 +307,7 @@ class ActivityLaunchAnimator(context: Context) {
@VisibleForTesting @VisibleForTesting
inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() { inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
private val launchContainer = controller.launchContainer private val launchContainer = controller.launchContainer
@PublishedApi internal val context = launchContainer.context private val context = launchContainer.context
private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer) private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
private var animator: ValueAnimator? = null private var animator: ValueAnimator? = null
@@ -294,7 +323,6 @@ class ActivityLaunchAnimator(context: Context) {
// posting it. // posting it.
private var onTimeout = Runnable { onAnimationTimedOut() } private var onTimeout = Runnable { onAnimationTimedOut() }
@PublishedApi
internal fun postTimeout() { internal fun postTimeout() {
launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT) launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT)
} }
@@ -336,11 +364,13 @@ class ActivityLaunchAnimator(context: Context) {
remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>, remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
iCallback: IRemoteAnimationFinishedCallback iCallback: IRemoteAnimationFinishedCallback
) { ) {
Log.d(TAG, "Remote animation started")
val window = remoteAnimationTargets.firstOrNull { val window = remoteAnimationTargets.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")
removeTimeout() removeTimeout()
invokeCallback(iCallback) invokeCallback(iCallback)
controller.onLaunchAnimationCancelled() controller.onLaunchAnimationCancelled()
@@ -399,10 +429,12 @@ class ActivityLaunchAnimator(context: Context) {
animator.addListener(object : AnimatorListenerAdapter() { animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) { override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
Log.d(TAG, "Animation started")
controller.onLaunchAnimationStart(isExpandingFullyAbove) controller.onLaunchAnimationStart(isExpandingFullyAbove)
} }
override fun onAnimationEnd(animation: Animator?) { override fun onAnimationEnd(animation: Animator?) {
Log.d(TAG, "Animation ended")
invokeCallback(iCallback) invokeCallback(iCallback)
controller.onLaunchAnimationEnd(isExpandingFullyAbove) controller.onLaunchAnimationEnd(isExpandingFullyAbove)
} }
@@ -496,6 +528,7 @@ class ActivityLaunchAnimator(context: Context) {
return return
} }
Log.d(TAG, "Remote animation timed out")
timedOut = true timedOut = true
controller.onLaunchAnimationCancelled() controller.onLaunchAnimationCancelled()
} }
@@ -505,6 +538,7 @@ class ActivityLaunchAnimator(context: Context) {
return return
} }
Log.d(TAG, "Remote animation was cancelled")
cancelled = true cancelled = true
removeTimeout() removeTimeout()
context.mainExecutor.execute { context.mainExecutor.execute {

View File

@@ -2801,7 +2801,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(), (adapter) -> { areLaunchAnimationsEnabled(), intent.getPackage(), (adapter) -> {
ActivityOptions options = new ActivityOptions( ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, adapter)); getActivityOptions(mDisplayId, adapter));
options.setDisallowEnterPictureInPictureWhileLaunching( options.setDisallowEnterPictureInPictureWhileLaunching(
@@ -4565,7 +4565,7 @@ public class StatusBar extends SystemUI implements DemoMode,
animationController, this, intent.isActivity()) : null; animationController, this, intent.isActivity()) : null;
mActivityLaunchAnimator.startPendingIntentWithAnimation( mActivityLaunchAnimator.startPendingIntentWithAnimation(
controller, areLaunchAnimationsEnabled(), controller, areLaunchAnimationsEnabled(), 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) {

View File

@@ -437,7 +437,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController, mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
!wasOccluded && mStatusBar.areLaunchAnimationsEnabled(), !wasOccluded && mStatusBar.areLaunchAnimationsEnabled(),
(adapter) -> { intent.getCreatorPackage(), (adapter) -> {
long eventTime = row.getAndResetLastActionUpTime(); long eventTime = row.getAndResetLastActionUpTime();
Bundle options = eventTime > 0 Bundle options = eventTime > 0
? getActivityOptions( ? getActivityOptions(
@@ -469,6 +469,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mActivityLaunchAnimator.startIntentWithAnimation( mActivityLaunchAnimator.startIntentWithAnimation(
animationController, mStatusBar.areLaunchAnimationsEnabled(), animationController, mStatusBar.areLaunchAnimationsEnabled(),
intent.getPackage(),
(adapter) -> TaskStackBuilder.create(mContext) (adapter) -> TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent) .addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions( .startActivities(getActivityOptions(
@@ -499,7 +500,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
true /* isActivityIntent */); true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(animationController, mActivityLaunchAnimator.startIntentWithAnimation(animationController,
mStatusBar.areLaunchAnimationsEnabled(), mStatusBar.areLaunchAnimationsEnabled(), intent.getPackage(),
(adapter) -> tsb.startActivities( (adapter) -> tsb.startActivities(
getActivityOptions(mStatusBar.getDisplayId(), adapter), getActivityOptions(mStatusBar.getDisplayId(), adapter),
UserHandle.CURRENT)); UserHandle.CURRENT));

View File

@@ -51,7 +51,11 @@ 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(controller, animate, intentStarter) activityLaunchAnimator.startIntentWithAnimation(
controller = controller,
animate = animate,
intentStarter = intentStarter
)
}.join() }.join()
} }

View File

@@ -269,7 +269,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
verify(mShadeController, atLeastOnce()).collapsePanel(); verify(mShadeController, atLeastOnce()).collapsePanel();
verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(), verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(),
eq(false) /* animate */, any()); eq(false) /* animate */, any(), any());
verify(mAssistManager).hideAssist(); verify(mAssistManager).hideAssist();