Waiting until onResume before creating the wallpaper-open animation.

Bug: 77853906
Change-Id: I5126855492da59c9dbfef0ce2a97f94662a6522b
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index cd67300..c985354 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3;
 
+import static com.android.launcher3.Utilities.postAsyncCallback;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -28,16 +30,12 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 @TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner extends AnimatorListenerAdapter
-        implements RemoteAnimationRunnerCompat {
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
 
     private static final int REFRESH_RATE_MS = 16;
 
     private final Handler mHandler;
-
-    private Runnable mSysFinishRunnable;
-
-    private AnimatorSet mAnimator;
+    private AnimationResult mAnimationResult;
 
     public LauncherAnimationRunner(Handler handler) {
         mHandler = handler;
@@ -46,34 +44,26 @@
     @BinderThread
     @Override
     public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
-        mHandler.post(() -> {
-            // Finish any previous animation
-            finishSystemAnimation();
-
-            mSysFinishRunnable = runnable;
-            mAnimator = getAnimator(targetCompats);
-            if (mAnimator == null) {
-                finishSystemAnimation();
-                return;
-            }
-            mAnimator.addListener(this);
-            mAnimator.start();
-            // Because t=0 has the app icon in its original spot, we can skip the
-            // first frame and have the same movement one frame earlier.
-            mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
-
+        postAsyncCallback(mHandler, () -> {
+            finishExistingAnimation();
+            mAnimationResult = new AnimationResult(runnable);
+            onCreateAnimation(targetCompats, mAnimationResult);
         });
     }
 
+    /**
+     * Called on the UI thread when the animation targets are received. The implementation must
+     * call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run.
+     */
     @UiThread
-    public abstract AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats);
+    public abstract void onCreateAnimation(
+            RemoteAnimationTargetCompat[] targetCompats, AnimationResult result);
 
     @UiThread
-    @Override
-    public void onAnimationEnd(Animator animation) {
-        if (animation == mAnimator) {
-            mAnimator = null;
-            finishSystemAnimation();
+    private void finishExistingAnimation() {
+        if (mAnimationResult != null) {
+            mAnimationResult.finish();
+            mAnimationResult = null;
         }
     }
 
@@ -83,20 +73,55 @@
     @BinderThread
     @Override
     public void onAnimationCancelled() {
-        mHandler.post(() -> {
-            if (mAnimator != null) {
-                mAnimator.removeListener(this);
-                mAnimator.end();
-                mAnimator = null;
-            }
-        });
+        postAsyncCallback(mHandler, this::finishExistingAnimation);
     }
 
-    @UiThread
-    private void finishSystemAnimation() {
-        if (mSysFinishRunnable != null) {
-            mSysFinishRunnable.run();
-            mSysFinishRunnable = null;
+    public static final class AnimationResult {
+
+        private final Runnable mFinishRunnable;
+
+        private AnimatorSet mAnimator;
+        private boolean mFinished = false;
+        private boolean mInitialized = false;
+
+        private AnimationResult(Runnable finishRunnable) {
+            mFinishRunnable = finishRunnable;
+        }
+
+        @UiThread
+        private void finish() {
+            if (!mFinished) {
+                mFinishRunnable.run();
+                mFinished = true;
+            }
+        }
+
+        @UiThread
+        public void setAnimation(AnimatorSet animation) {
+            if (mInitialized) {
+                throw new IllegalStateException("Animation already initialized");
+            }
+            mInitialized = true;
+            mAnimator = animation;
+            if (mAnimator == null) {
+                finish();
+            } else if (mFinished) {
+                // Animation callback was already finished, skip the animation.
+                mAnimator.end();
+            } else {
+                // Start the animation
+                mAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        finish();
+                    }
+                });
+                mAnimator.start();
+
+                // Because t=0 has the app icon in its original spot, we can skip the
+                // first frame and have the same movement one frame earlier.
+                mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 299e7d5..3ff9921 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
@@ -163,7 +164,8 @@
             RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler) {
 
                 @Override
-                public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+                public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                        AnimationResult result) {
                     AnimatorSet anim = new AnimatorSet();
 
                     boolean launcherClosing =
@@ -185,7 +187,7 @@
                         anim.addListener(mForceInvisibleListener);
                     }
 
-                    return anim;
+                    result.setAnimation(anim);
                 }
             };
 
@@ -558,7 +560,16 @@
     private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
         return new LauncherAnimationRunner(mHandler) {
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                if (!mLauncher.hasBeenResumed()) {
+                    // If launcher is not resumed, wait until new async-frame after resume
+                    mLauncher.setOnResumeCallback(() ->
+                            postAsyncCallback(mHandler, () ->
+                                    onCreateAnimation(targetCompats, result)));
+                    return;
+                }
+
                 AnimatorSet anim = null;
                 RemoteAnimationProvider provider = mRemoteAnimationProvider;
                 if (provider != null) {
@@ -586,7 +597,7 @@
                 }
 
                 mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
-                return anim;
+                result.setAnimation(anim);
             }
         };
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 820875c..551984a 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -168,8 +168,9 @@
         RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler) {
 
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
-                return composeRecentsLaunchAnimator(taskView, targetCompats);
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                result.setAnimation(composeRecentsLaunchAnimator(taskView, targetCompats));
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 2709523..d456367 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -16,13 +16,12 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.systemui.shared.recents.utilities.Utilities
-        .postAtFrontOfQueueAsynchronously;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
 import android.animation.Animator;
@@ -248,7 +247,7 @@
         if (Looper.myLooper() == handler.getLooper()) {
             mStateCallback.setState(stateFlag);
         } else {
-            postAtFrontOfQueueAsynchronously(handler, () -> mStateCallback.setState(stateFlag));
+            postAsyncCallback(handler, () -> mStateCallback.setState(stateFlag));
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 2ffcae3..7ee16c1 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -32,9 +32,11 @@
 
     default ActivityOptions toActivityOptions(Handler handler, long duration) {
         LauncherAnimationRunner runner = new LauncherAnimationRunner(handler) {
+
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
-                return createWindowAnimation(targetCompats);
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                result.setAnimation(createWindowAnimation(targetCompats));
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 1f1ef9a..f197921 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -56,8 +56,23 @@
     protected UserEventDispatcher mUserEventDispatcher;
     protected SystemUiController mSystemUiController;
 
-    private boolean mStarted;
-    private boolean mUserActive;
+    private static final int ACTIVITY_STATE_STARTED = 1 << 0;
+    private static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+    /**
+     * State flag indicating if the user is active or the actitvity when to background as a result
+     * of user action.
+     * @see #isUserActive()
+     */
+    private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2;
+
+    @Retention(SOURCE)
+    @IntDef(
+            flag = true,
+            value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE})
+    public @interface ActivityFlags{}
+
+    @ActivityFlags
+    private int mActivityFlags;
 
     // When the recents animation is running, the visibility of the Launcher is managed by the
     // animation
@@ -103,19 +118,19 @@
 
     @Override
     protected void onStart() {
-        mStarted = true;
+        mActivityFlags |= ACTIVITY_STATE_STARTED;
         super.onStart();
     }
 
     @Override
     protected void onResume() {
-        mUserActive = true;
+        mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE;
         super.onResume();
     }
 
     @Override
     protected void onUserLeaveHint() {
-        mUserActive = false;
+        mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE;
         super.onUserLeaveHint();
     }
 
@@ -129,17 +144,30 @@
 
     @Override
     protected void onStop() {
-        mStarted = false;
+        mActivityFlags &= ~ACTIVITY_STATE_STARTED;
         mForceInvisible = 0;
         super.onStop();
     }
 
+    @Override
+    protected void onPause() {
+        mActivityFlags &= ~ACTIVITY_STATE_RESUMED;
+        super.onPause();
+    }
+
     public boolean isStarted() {
-        return mStarted;
+        return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
+    }
+
+    /**
+     * isResumed in already defined as a hidden final method in Activity.java
+     */
+    public boolean hasBeenResumed() {
+        return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
     }
 
     public boolean isUserActive() {
-        return mUserActive;
+        return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
     }
 
     public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ba96d4a..98440ff 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -35,6 +35,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
 import android.support.v4.os.BuildCompat;
@@ -575,4 +577,12 @@
         return hashSet;
     }
 
+    /**
+     * Utility method to post a runnable on the handler, skipping the synchronization barriers.
+     */
+    public static void postAsyncCallback(Handler handler, Runnable callback) {
+        Message msg = Message.obtain(handler, callback);
+        msg.setAsynchronous(true);
+        handler.sendMessage(msg);
+    }
 }