Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e8dfa69 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Register the remote animation before starting an intent.

This CL calls
IActivityTaskManager#registerRemoteAnimationForNextActivityStart before
starting an intent for which the launch should be animated. This allows
to also animate trampoline launches.

This CL also adds a bit of logging to ActivityLaunchAnimator.

Bug: 184726377
Test: Manual
Change-Id: I4a3b95ed5e93e870d6ae0989d9c1ec771c9e85fc
parent 63a14538
Loading
Loading
Loading
Loading
+39 −5
Original line number Diff line number Diff line
@@ -4,12 +4,14 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.app.PendingIntent
import android.content.Context
import android.graphics.Matrix
import android.graphics.Rect
import android.os.Looper
import android.os.RemoteException
import android.util.Log
import android.util.MathUtils
import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner
@@ -30,6 +32,8 @@ import kotlin.math.roundToInt
 * nicely into the starting window.
 */
class ActivityLaunchAnimator(context: Context) {
    private val TAG = this::class.java.simpleName

    companion object {
        const val ANIMATION_DURATION = 500L
        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
     * 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].
     */
    @JvmOverloads
    inline fun startIntentWithAnimation(
    fun startIntentWithAnimation(
        controller: Controller?,
        animate: Boolean = true,
        packageName: String? = null,
        intentStarter: (RemoteAnimationAdapter?) -> Int
    ) {
        if (controller == null || !animate) {
            Log.d(TAG, "Starting intent with no animation")
            intentStarter(null)
            controller?.callOnIntentStartedOnMainThread(willAnimate = false)
            return
        }

        Log.d(TAG, "Starting intent with a launch animation")
        val runner = Runner(controller)
        val animationAdapter = RemoteAnimationAdapter(
            runner,
            ANIMATION_DURATION,
            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 willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
            launchResult == ActivityManager.START_SUCCESS

        Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate")
        controller.callOnIntentStartedOnMainThread(willAnimate)

        // 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) {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            this.launchContainer.context.mainExecutor.execute {
@@ -125,15 +148,21 @@ class ActivityLaunchAnimator(context: Context) {
     * Same as [startIntentWithAnimation] but allows [intentStarter] to throw a
     * [PendingIntent.CanceledException] which must then be handled by the caller. This is useful
     * 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)
    @JvmOverloads
    fun startPendingIntentWithAnimation(
        controller: Controller?,
        animate: Boolean = true,
        packageName: String? = null,
        intentStarter: PendingIntentStarter
    ) {
        startIntentWithAnimation(controller, animate) { intentStarter.startPendingIntent(it) }
        startIntentWithAnimation(controller, animate, packageName) {
            intentStarter.startPendingIntent(it)
        }
    }

    /** Create a new animation [Runner] controlled by [controller]. */
@@ -278,7 +307,7 @@ class ActivityLaunchAnimator(context: Context) {
    @VisibleForTesting
    inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
        private val launchContainer = controller.launchContainer
        @PublishedApi internal val context = launchContainer.context
        private val context = launchContainer.context
        private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
        private var animator: ValueAnimator? = null

@@ -294,7 +323,6 @@ class ActivityLaunchAnimator(context: Context) {
        // posting it.
        private var onTimeout = Runnable { onAnimationTimedOut() }

        @PublishedApi
        internal fun postTimeout() {
            launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT)
        }
@@ -336,11 +364,13 @@ class ActivityLaunchAnimator(context: Context) {
            remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
            iCallback: IRemoteAnimationFinishedCallback
        ) {
            Log.d(TAG, "Remote animation started")
            val window = remoteAnimationTargets.firstOrNull {
                it.mode == RemoteAnimationTarget.MODE_OPENING
            }

            if (window == null) {
                Log.d(TAG, "Aborting the animation as no window is opening")
                removeTimeout()
                invokeCallback(iCallback)
                controller.onLaunchAnimationCancelled()
@@ -399,10 +429,12 @@ class ActivityLaunchAnimator(context: Context) {

            animator.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
                    Log.d(TAG, "Animation started")
                    controller.onLaunchAnimationStart(isExpandingFullyAbove)
                }

                override fun onAnimationEnd(animation: Animator?) {
                    Log.d(TAG, "Animation ended")
                    invokeCallback(iCallback)
                    controller.onLaunchAnimationEnd(isExpandingFullyAbove)
                }
@@ -496,6 +528,7 @@ class ActivityLaunchAnimator(context: Context) {
                return
            }

            Log.d(TAG, "Remote animation timed out")
            timedOut = true
            controller.onLaunchAnimationCancelled()
        }
@@ -505,6 +538,7 @@ class ActivityLaunchAnimator(context: Context) {
                return
            }

            Log.d(TAG, "Remote animation was cancelled")
            cancelled = true
            removeTimeout()
            context.mainExecutor.execute {
+2 −2
Original line number Diff line number Diff line
@@ -2801,7 +2801,7 @@ public class StatusBar extends SystemUI implements DemoMode,
            int[] result = new int[]{ActivityManager.START_CANCELED};

            mActivityLaunchAnimator.startIntentWithAnimation(animController,
                    areLaunchAnimationsEnabled(), (adapter) -> {
                    areLaunchAnimationsEnabled(), intent.getPackage(), (adapter) -> {
                        ActivityOptions options = new ActivityOptions(
                                getActivityOptions(mDisplayId, adapter));
                        options.setDisallowEnterPictureInPictureWhileLaunching(
@@ -4563,7 +4563,7 @@ public class StatusBar extends SystemUI implements DemoMode,
                                animationController, this, intent.isActivity()) : null;

                mActivityLaunchAnimator.startPendingIntentWithAnimation(
                        controller, areLaunchAnimationsEnabled(),
                        controller, areLaunchAnimationsEnabled(), intent.getCreatorPackage(),
                        (animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
                                null, getActivityOptions(mDisplayId, animationAdapter)));
            } catch (PendingIntent.CanceledException e) {
+3 −2
Original line number Diff line number Diff line
@@ -437,7 +437,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit

            mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
                    !wasOccluded && mStatusBar.areLaunchAnimationsEnabled(),
                    (adapter) -> {
                    intent.getCreatorPackage(), (adapter) -> {
                        long eventTime = row.getAndResetLastActionUpTime();
                        Bundle options = eventTime > 0
                                ? getActivityOptions(
@@ -469,6 +469,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit

                mActivityLaunchAnimator.startIntentWithAnimation(
                        animationController, mStatusBar.areLaunchAnimationsEnabled(),
                        intent.getPackage(),
                        (adapter) -> TaskStackBuilder.create(mContext)
                                .addNextIntentWithParentStack(intent)
                                .startActivities(getActivityOptions(
@@ -499,7 +500,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
                                true /* isActivityIntent */);

                mActivityLaunchAnimator.startIntentWithAnimation(animationController,
                        mStatusBar.areLaunchAnimationsEnabled(),
                        mStatusBar.areLaunchAnimationsEnabled(), intent.getPackage(),
                        (adapter) -> tsb.startActivities(
                                getActivityOptions(mStatusBar.getDisplayId(), adapter),
                                UserHandle.CURRENT));
+5 −1
Original line number Diff line number Diff line
@@ -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
        // thread.
        thread {
            activityLaunchAnimator.startIntentWithAnimation(controller, animate, intentStarter)
            activityLaunchAnimator.startIntentWithAnimation(
                    controller = controller,
                    animate = animate,
                    intentStarter = intentStarter
            )
        }.join()
    }

+1 −1
Original line number Diff line number Diff line
@@ -269,7 +269,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
        verify(mShadeController, atLeastOnce()).collapsePanel();

        verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(),
                eq(false) /* animate */, any());
                eq(false) /* animate */, any(), any());

        verify(mAssistManager).hideAssist();