Merge "Adding tracing for switching to overview instead of all apps" into ub-launcher3-rvc-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 1515ffa..ea0d840 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -120,7 +120,7 @@
     protected MultiStateCallback mStateCallback;
 
     protected boolean mCanceled;
-    protected int mFinishingRecentsAnimationForNewTaskId = -1;
+    protected int mLastStartedTaskId = -1;
 
     private RecentsOrientedState mOrientedState;
 
@@ -199,7 +199,7 @@
                         mRecentsAnimationTargets));
     }
 
-    protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
+    protected void startNewTask(Consumer<Boolean> resultCallback) {
         // Launch the task user scrolled to (mRecentsView.getNextPage()).
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             // We finish recents animation inside launchTask() when live tile is enabled.
@@ -210,18 +210,17 @@
             if (!mCanceled) {
                 TaskView nextTask = mRecentsView.getTaskView(taskId);
                 if (nextTask != null) {
+                    mLastStartedTaskId = taskId;
                     nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
                             success -> {
                                 resultCallback.accept(success);
                                 if (!success) {
                                     mActivityInterface.onLaunchTaskFailed();
                                     nextTask.notifyTaskLaunchFailed(TAG);
-                                } else {
-                                    mActivityInterface.onLaunchTaskSuccess();
+                                    mRecentsAnimationController.finish(true /* toRecents */, null);
                                 }
                             }, MAIN_EXECUTOR.getHandler());
                 }
-                mStateCallback.setStateOnUiThread(successStateFlag);
             }
             mCanceled = false;
         }
@@ -241,6 +240,7 @@
     }
 
     /**
+     * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
      * @return whether the recents animation has started and there are valid app targets.
      */
     protected boolean hasTargets() {
@@ -306,6 +306,30 @@
         }
     }
 
+    @Override
+    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        if (mRecentsAnimationController != null) {
+            if (handleTaskAppeared(appearedTaskTarget)) {
+                mRecentsAnimationController.finish(false /* toRecents */,
+                        null /* onFinishComplete */);
+                mActivityInterface.onLaunchTaskSuccess();
+            }
+        }
+    }
+
+    /** @return Whether this was the task we were waiting to appear, and thus handled it. */
+    protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
+
+    /**
+     * @return The index of the TaskView in RecentsView whose taskId matches the task that will
+     * resume if we finish the controller.
+     */
+    protected int getLastAppearedTaskIndex() {
+        return mGestureState.getLastAppearedTaskId() != -1
+                ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
+                : mRecentsView.getRunningTaskIndex();
+    }
+
     private Rect getStackBounds(DeviceProfile dp) {
         if (mActivity != null) {
             int loc[] = new int[2];
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 894bd0c..d24b16a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -46,11 +46,11 @@
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 /**
  * Handles the navigation gestures when a 3rd party launcher is the default home activity.
@@ -320,15 +320,6 @@
             }
 
             if (mRecentsView != null) {
-                if (mFinishingRecentsAnimationForNewTaskId != -1) {
-                    TaskView newRunningTaskView = mRecentsView.getTaskView(
-                            mFinishingRecentsAnimationForNewTaskId);
-                    int newRunningTaskId = newRunningTaskView != null
-                            ? newRunningTaskView.getTask().key.id
-                            : -1;
-                    mRecentsView.setCurrentTask(newRunningTaskId);
-                    mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
-                }
                 mRecentsView.setOnScrollChangeListener(null);
             }
         } else {
@@ -401,7 +392,7 @@
                 break;
             }
             case NEW_TASK: {
-                startNewTask(STATE_HANDLER_INVALIDATED, b -> {});
+                startNewTask(success -> { });
                 break;
             }
         }
@@ -416,7 +407,7 @@
             if (mRecentsView == null || !hasTargets()) {
                 mGestureState.setEndTarget(LAST_TASK);
             } else {
-                final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+                final int runningTaskIndex = getLastAppearedTaskIndex();
                 final int taskToLaunch = mRecentsView.getNextPage();
                 mGestureState.setEndTarget(
                         (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
@@ -494,6 +485,11 @@
         super.onRecentsAnimationCanceled(thumbnailData);
     }
 
+    @Override
+    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        return true;
+    }
+
     /**
      * Creates an animation that transforms the current app window into the home app.
      * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index a9c4f4a..e0393c7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -32,7 +32,6 @@
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
-import static com.android.quickstep.GestureState.STATE_TASK_APPEARED_DURING_SWITCH;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
@@ -264,8 +263,6 @@
                         | STATE_RECENTS_SCROLLING_FINISHED,
                 this::onSettledOnEndTarget);
 
-        mGestureState.runOnceAtState(STATE_TASK_APPEARED_DURING_SWITCH, this::onTaskAppeared);
-
         mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
         mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                 this::invalidateHandlerWithLauncher);
@@ -769,20 +766,16 @@
         }
     }
 
-    private void onTaskAppeared() {
-        RemoteAnimationTargetCompat app = mGestureState.getAnimationTarget();
-        if (mRecentsAnimationController != null && app != null) {
-
-            // TODO(b/152480470): Update Task target animation after onTaskAppeared holistically.
-            /* android.util.Log.d("LauncherSwipeHandler", "onTaskAppeared");
-
-            final boolean result = mRecentsAnimationController.removeTaskTarget(app);
-            mGestureState.setAnimationTarget(null);
-            android.util.Log.d("LauncherSwipeHandler", "removeTask, result=" + result); */
-
-            mRecentsAnimationController.finish(false /* toRecents */,
-                    null /* onFinishComplete */);
+    @Override
+    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return false;
         }
+        if (appearedTaskTarget.taskId == mLastStartedTaskId) {
+            reset();
+            return true;
+        }
+        return false;
     }
 
     private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
@@ -1029,12 +1022,22 @@
                         // skip doing any future work here for the current gesture.
                         return;
                     }
-                    if (target == NEW_TASK && mRecentsView != null
-                            && mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
-                        // We are about to launch the current running task, so use LAST_TASK state
-                        // instead of NEW_TASK. This could happen, for example, if our scroll is
-                        // aborted after we determined the target to be NEW_TASK.
-                        mGestureState.setEndTarget(LAST_TASK);
+                    if (mRecentsView != null) {
+                        int taskToLaunch = mRecentsView.getNextPage();
+                        int runningTask = getLastAppearedTaskIndex();
+                        if (target == NEW_TASK && taskToLaunch == runningTask) {
+                            // We are about to launch the current running task, so use LAST_TASK
+                            // state instead of NEW_TASK. This could happen, for example, if our
+                            // scroll is aborted after we determined the target to be NEW_TASK.
+                            mGestureState.setEndTarget(LAST_TASK);
+                        } else if (target == LAST_TASK && taskToLaunch != runningTask) {
+                            // We are about to re-launch the previously running task, but we can't
+                            // just finish the controller like we normally would because that would
+                            // instead resume the last task that appeared. As a workaround, launch
+                            // the task as if it were a new task.
+                            // TODO: is this expected?
+                            mGestureState.setEndTarget(NEW_TASK);
+                        }
                     }
                     mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
                 }
@@ -1158,8 +1161,9 @@
 
     @UiThread
     private void startNewTaskInternal() {
-        startNewTask(STATE_HANDLER_INVALIDATED, success -> {
+        startNewTask(success -> {
             if (!success) {
+                reset();
                 // We couldn't launch the task, so take user to overview so they can
                 // decide what to do instead of staying in this broken state.
                 endLauncherTransitionController();
@@ -1184,19 +1188,6 @@
                 .getAnimationPlayer().isStarted()) {
             mLauncherTransitionController.getAnimationPlayer().cancel();
         }
-
-        if (mFinishingRecentsAnimationForNewTaskId != -1) {
-            // If we are canceling mid-starting a new task, switch to the screenshot since the
-            // recents animation has finished
-            switchToScreenshot();
-            TaskView newRunningTaskView = mRecentsView.getTaskView(
-                    mFinishingRecentsAnimationForNewTaskId);
-            int newRunningTaskId = newRunningTaskView != null
-                    ? newRunningTaskView.getTask().key.id
-                    : -1;
-            mRecentsView.setCurrentTask(newRunningTaskId);
-            mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
-        }
     }
 
     private void invalidateHandler() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index ce7a141..8eb15cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -30,8 +30,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
 import android.annotation.TargetApi;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.Service;
@@ -518,8 +516,12 @@
     private GestureState createGestureState() {
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
-        gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
-                () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
+        if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+            gestureState.updateRunningTask(mGestureState.getRunningTask());
+        } else {
+            gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
+                    () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
+        }
         return gestureState;
     }
 
@@ -637,14 +639,7 @@
                     runningComponent != null && runningComponent.equals(homeComponent);
         }
 
-        if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
-            // If the finish animation was interrupted, then continue using the other activity input
-            // consumer but with the next task as the running task
-            RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
-            info.id = previousGestureState.getFinishingRecentsAnimationTaskId();
-            gestureState.updateRunningTask(info);
-            return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
-        } else if (gestureState.getRunningTask() == null) {
+        if (gestureState.getRunningTask() == null) {
             return mResetGestureInputConsumer;
         } else if (previousGestureState.isRunningAnimationToLauncher()
                 || gestureState.getActivityInterface().isResumed()
@@ -658,25 +653,22 @@
         } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
             return mResetGestureInputConsumer;
         } else {
-            return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
+            return createOtherActivityInputConsumer(gestureState, event);
         }
     }
 
-    private InputConsumer createOtherActivityInputConsumer(GestureState previousGestureState,
-            GestureState gestureState, MotionEvent event) {
+    private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
+            MotionEvent event) {
 
-        final boolean shouldDefer;
         final BaseSwipeUpHandler.Factory factory;
-
         if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
-            shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
             factory = mFallbackSwipeHandlerFactory;
         } else {
-            shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
-                    event);
             factory = mLauncherSwipeHandlerFactory;
         }
 
+        final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
+                || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
         final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
         return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
                 gestureState, shouldDefer, this::onConsumerInactive,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 4f6f970..a93d2aa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -977,7 +977,15 @@
     }
 
     public int getRunningTaskIndex() {
-        TaskView tv = getRunningTaskView();
+        return getTaskIndexForId(mRunningTaskId);
+    }
+
+    /**
+     * Get the index of the task view whose id matches {@param taskId}.
+     * @return -1 if there is no task view for the task id, else the index of the task view.
+     */
+    public int getTaskIndexForId(int taskId) {
+        TaskView tv = getTaskView(taskId);
         return tv == null ? -1 : indexOfChild(tv);
     }
 
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 544f420..f06e1a6 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 
 import android.app.ActivityManager;
@@ -110,10 +111,6 @@
     public static final int STATE_RECENTS_SCROLLING_FINISHED =
             getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
 
-    // Called when the new task appeared from quick switching.
-    public static final int STATE_TASK_APPEARED_DURING_SWITCH =
-            getFlagForIndex("STATE_TASK_APPEARED_DURING_SWITCH");
-
     // Needed to interact with the current activity
     private final Intent mHomeIntent;
     private final Intent mOverviewIntent;
@@ -123,9 +120,7 @@
 
     private ActivityManager.RunningTaskInfo mRunningTask;
     private GestureEndTarget mEndTarget;
-    private RemoteAnimationTargetCompat mAnimationTarget;
-    // TODO: This can be removed once we stop finishing the animation when starting a new task
-    private int mFinishingRecentsAnimationTaskId = -1;
+    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
 
     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
         mHomeIntent = componentObserver.getHomeIntent();
@@ -143,7 +138,7 @@
         mGestureId = other.mGestureId;
         mRunningTask = other.mRunningTask;
         mEndTarget = other.mEndTarget;
-        mFinishingRecentsAnimationTaskId = other.mFinishingRecentsAnimationTaskId;
+        mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
     }
 
     public GestureState() {
@@ -226,20 +221,26 @@
     }
 
     /**
+     * Updates the last task that appeared during this gesture.
+     */
+    public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
+        mLastAppearedTaskTarget = lastAppearedTaskTarget;
+    }
+
+    /**
+     * @return The id of the task that appeared during this gesture.
+     */
+    public int getLastAppearedTaskId() {
+        return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
+    }
+
+    /**
      * @return the end target for this gesture (if known).
      */
     public GestureEndTarget getEndTarget() {
         return mEndTarget;
     }
 
-    public void setAnimationTarget(RemoteAnimationTargetCompat target) {
-        mAnimationTarget = target;
-    }
-
-    public RemoteAnimationTargetCompat getAnimationTarget() {
-        return mAnimationTarget;
-    }
-
     /**
      * Sets the end target of this gesture and immediately notifies the state changes.
      */
@@ -260,29 +261,8 @@
     }
 
     /**
-     * @return the id for the task that was about to be launched following the finish of the recents
-     * animation.  Only defined between when the finish-recents call was made and the launch
-     * activity call is made.
-     */
-    public int getFinishingRecentsAnimationTaskId() {
-        return mFinishingRecentsAnimationTaskId;
-    }
-
-    /**
-     * Sets the id for the task will be launched after the recents animation is finished. Once the
-     * animation has finished then the id will be reset to -1.
-     */
-    public void setFinishingRecentsAnimationTaskId(int taskId) {
-        mFinishingRecentsAnimationTaskId = taskId;
-        mStateCallback.runOnceAtState(STATE_RECENTS_ANIMATION_FINISHED, () -> {
-            mFinishingRecentsAnimationTaskId = -1;
-        });
-    }
-
-    /**
      * @return whether the current gesture is still running a recents animation to a state in the
      *         Launcher or Recents activity.
-     * Updates the running task for the gesture to be the given {@param runningTask}.
      */
     public boolean isRunningAnimationToLauncher() {
         return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
@@ -314,18 +294,12 @@
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
     }
 
-    @Override
-    public void onTaskAppeared(RemoteAnimationTargetCompat app) {
-        mAnimationTarget = app;
-        mStateCallback.setState(STATE_TASK_APPEARED_DURING_SWITCH);
-    }
-
     public void dump(PrintWriter pw) {
         pw.println("GestureState:");
         pw.println("  gestureID=" + mGestureId);
         pw.println("  runningTask=" + mRunningTask);
         pw.println("  endTarget=" + mEndTarget);
-        pw.println("  finishingRecentsAnimationTaskId=" + mFinishingRecentsAnimationTaskId);
+        pw.println("  lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 103ea4e..a21c714 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -159,6 +159,6 @@
         /**
          * Callback made when a task started from the recents is ready for an app transition.
          */
-        default void onTaskAppeared(RemoteAnimationTargetCompat app) {}
+        default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {}
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 21e8c92..cad51f4 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
+import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
 
 import android.content.Intent;
 import android.util.Log;
@@ -28,6 +29,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
 
@@ -36,6 +38,7 @@
     private RecentsAnimationTargets mTargets;
     // Temporary until we can hook into gesture state events
     private GestureState mLastGestureState;
+    private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
 
     /**
      * Preloads the recents animation.
@@ -79,6 +82,8 @@
                 }
                 mController = controller;
                 mTargets = targets;
+                mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId());
+                mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
             }
 
             @Override
@@ -96,6 +101,20 @@
             public void onRecentsAnimationFinished(RecentsAnimationController controller) {
                 cleanUpRecentsAnimation(null /* canceledThumbnail */);
             }
+
+            @Override
+            public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+                if (mController != null) {
+                    if (mLastAppearedTaskTarget == null
+                            || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
+                        if (mLastAppearedTaskTarget != null) {
+                            mController.removeTaskTarget(mLastAppearedTaskTarget);
+                        }
+                        mLastAppearedTaskTarget = appearedTaskTarget;
+                        mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
+                    }
+                }
+            }
         });
         mCallbacks.addListener(gestureState);
         mCallbacks.addListener(listener);
@@ -112,6 +131,9 @@
         mCallbacks.removeListener(mLastGestureState);
         mLastGestureState = gestureState;
         mCallbacks.addListener(gestureState);
+        gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED
+                | STATE_RECENTS_ANIMATION_STARTED);
+        gestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
         return mCallbacks;
     }
 
@@ -171,6 +193,7 @@
         mCallbacks = null;
         mTargets = null;
         mLastGestureState = null;
+        mLastAppearedTaskTarget = null;
     }
 
     public void dump() {