Merge "Restart running task if another task was started but not appeared" 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 eb976c7..dd41d25 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -40,6 +40,7 @@
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -128,7 +129,6 @@
     protected MultiStateCallback mStateCallback;
 
     protected boolean mCanceled;
-    protected int mLastStartedTaskId = -1;
 
     private boolean mRecentsViewScrollLinked = false;
 
@@ -218,11 +218,16 @@
             if (!mCanceled) {
                 TaskView nextTask = mRecentsView.getTaskView(taskId);
                 if (nextTask != null) {
-                    mLastStartedTaskId = taskId;
+                    mGestureState.updateLastStartedTaskId(taskId);
                     nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
                             success -> {
                                 resultCallback.accept(success);
-                                if (!success) {
+                                if (success) {
+                                    if (mRecentsView.indexOfChild(nextTask)
+                                            == getLastAppearedTaskIndex()) {
+                                        onRestartLastAppearedTask();
+                                    }
+                                } else {
                                     mActivityInterface.onLaunchTaskFailed();
                                     nextTask.notifyTaskLaunchFailed(TAG);
                                     mRecentsAnimationController.finish(true /* toRecents */, null);
@@ -236,6 +241,19 @@
     }
 
     /**
+     * Called when we successfully startNewTask() on the task that was previously running. Normally
+     * we call resumeLastTask() when returning to the previously running task, but this handles a
+     * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+     * start A again to ensure it stays on top.
+     */
+    @CallSuper
+    protected void onRestartLastAppearedTask() {
+        // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+        // appeared.
+        mRecentsAnimationController.finish(false, null);
+    }
+
+    /**
      * Runs the given {@param action} if the recents animation has already started, or queues it to
      * be run when it is next started.
      */
@@ -331,6 +349,14 @@
                 : mRecentsView.getRunningTaskIndex();
     }
 
+    /**
+     * @return Whether we are continuing a gesture that already landed on a new task,
+     * but before that task appeared.
+     */
+    protected boolean hasStartedNewTask() {
+        return mGestureState.getLastStartedTaskId() != -1;
+    }
+
     protected void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
 
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 21c8c20..e1f34ed 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -725,7 +725,8 @@
         if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return false;
         }
-        if (appearedTaskTarget.taskId == mLastStartedTaskId) {
+        if (mGestureState.getEndTarget() == NEW_TASK
+                && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
             reset();
             return true;
         }
@@ -1016,17 +1017,19 @@
                     if (mRecentsView != null) {
                         int taskToLaunch = mRecentsView.getNextPage();
                         int runningTask = getLastAppearedTaskIndex();
-                        if (target == NEW_TASK && taskToLaunch == runningTask) {
+                        boolean hasStartedNewTask = hasStartedNewTask();
+                        if (target == NEW_TASK && taskToLaunch == runningTask
+                                && !hasStartedNewTask) {
                             // 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) {
+                        } else if (target == LAST_TASK && hasStartedNewTask) {
                             // 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?
+                            // instead resume the last task that appeared, and not ensure that this
+                            // task is restored to the top. To address this, re-launch the task as
+                            // if it were a new task.
                             mGestureState.setEndTarget(NEW_TASK);
                         }
                     }
@@ -1164,6 +1167,12 @@
         });
     }
 
+    @Override
+    protected void onRestartLastAppearedTask() {
+        super.onRestartLastAppearedTask();
+        reset();
+    }
+
     private void reset() {
         mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
     }
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 c44d9d7..4b2fc75 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -518,6 +518,7 @@
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
             gestureState.updateRunningTask(mGestureState.getRunningTask());
+            gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
         } else {
             gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
                     () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index f06e1a6..9b515ae 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -15,7 +15,6 @@
  */
 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;
@@ -121,6 +120,7 @@
     private ActivityManager.RunningTaskInfo mRunningTask;
     private GestureEndTarget mEndTarget;
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private int mLastStartedTaskId = -1;
 
     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
         mHomeIntent = componentObserver.getHomeIntent();
@@ -139,6 +139,7 @@
         mRunningTask = other.mRunningTask;
         mEndTarget = other.mEndTarget;
         mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
+        mLastStartedTaskId = other.mLastStartedTaskId;
     }
 
     public GestureState() {
@@ -235,6 +236,21 @@
     }
 
     /**
+     * Updates the last task that we started via startActivityFromRecents() during this gesture.
+     */
+    public void updateLastStartedTaskId(int lastStartedTaskId) {
+        mLastStartedTaskId = lastStartedTaskId;
+    }
+
+    /**
+     * @return The id of the task that was most recently started during this gesture, or -1 if
+     * no task has been started yet (i.e. we haven't settled on a new task).
+     */
+    public int getLastStartedTaskId() {
+        return mLastStartedTaskId;
+    }
+
+    /**
      * @return the end target for this gesture (if known).
      */
     public GestureEndTarget getEndTarget() {
@@ -300,6 +316,7 @@
         pw.println("  runningTask=" + mRunningTask);
         pw.println("  endTarget=" + mEndTarget);
         pw.println("  lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
+        pw.println("  lastStartedTaskId=" + mLastStartedTaskId);
         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
     }
 }