Merge "Task stabilization improvement" into ub-launcher3-master
diff --git a/quickstep/src/com/android/quickstep/TaskListStabilizer.java b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
index 0d23973..3eb26b4 100644
--- a/quickstep/src/com/android/quickstep/TaskListStabilizer.java
+++ b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
@@ -17,79 +17,153 @@
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
 
+import android.app.ActivityManager.RecentTaskInfo;
+import android.content.ComponentName;
+import android.os.Process;
 import android.os.SystemClock;
-import android.util.SparseArray;
+import android.util.Log;
 
 import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 public class TaskListStabilizer {
 
     private static final long TASK_CACHE_TIMEOUT_MS = 5000;
 
-    private final SparseArray<Task> mTempMap = new SparseArray<>();
-    private final IntArray mTempArray = new IntArray();
-    private final IntSet mTempSet = new IntSet();
+    private static final int INVALID_TASK_ID = -1;
 
-    private final IntArray mLastStableOrder = new IntArray();
-    private final IntSet mLastSet = new IntSet();
-    private final IntArray mLastUnstableOrder = new IntArray();
+    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
 
-    private long mLastReorderTime;
+        @Override
+        public void onTaskCreated(int taskId, ComponentName componentName) {
+            onTaskCreatedInternal(taskId);
+        }
 
-    public ArrayList<Task> reorder(ArrayList<Task> tasks) {
+        @Override
+        public void onTaskMovedToFront(int taskId) {
+            onTaskMovedToFrontInternal(taskId);
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            onTaskRemovedInternal(taskId);
+        }
+    };
+
+    // Task ids ordered based on recency, 0th index is the latest task
+    private final IntArray mOrderedTaskIds;
+
+    // Wrapper objects used for sorting tasks
+    private final ArrayList<TaskWrapper> mTaskWrappers = new ArrayList<>();
+
+    // Information about recent task re-order which has not been applied yet
+    private int mScheduledMoveTaskId = INVALID_TASK_ID;
+    private long mScheduledMoveTime = 0;
+
+    public TaskListStabilizer() {
+        if (ENABLE_TASK_STABILIZER.get()) {
+            // Initialize the task ids map
+            List<RecentTaskInfo> rawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
+                    Integer.MAX_VALUE, Process.myUserHandle().getIdentifier());
+            mOrderedTaskIds = new IntArray(rawTasks.size());
+            for (RecentTaskInfo info : rawTasks) {
+                mOrderedTaskIds.add(new TaskKey(info).id);
+            }
+
+            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        } else {
+            mOrderedTaskIds = null;
+        }
+    }
+
+    private synchronized void onTaskCreatedInternal(int taskId) {
+        applyScheduledMoveUnchecked();
+        mOrderedTaskIds.add(taskId);
+    }
+
+    private synchronized void onTaskRemovedInternal(int taskId) {
+        applyScheduledMoveUnchecked();
+        mOrderedTaskIds.removeValue(taskId);
+    }
+
+    private void applyScheduledMoveUnchecked() {
+        if (mScheduledMoveTaskId != INVALID_TASK_ID) {
+            // Mode the scheduled task to front
+            mOrderedTaskIds.removeValue(mScheduledMoveTaskId);
+            mOrderedTaskIds.add(mScheduledMoveTaskId);
+            mScheduledMoveTaskId = INVALID_TASK_ID;
+        }
+    }
+
+    /**
+     * Checks if the scheduled move has timed out and moves the task to front accordingly.
+     */
+    private void applyScheduledMoveIfTime() {
+        if (mScheduledMoveTaskId != INVALID_TASK_ID
+                && (SystemClock.uptimeMillis() - mScheduledMoveTime) > TASK_CACHE_TIMEOUT_MS) {
+            applyScheduledMoveUnchecked();
+        }
+    }
+
+    private synchronized void onTaskMovedToFrontInternal(int taskId) {
+        applyScheduledMoveIfTime();
+        mScheduledMoveTaskId = taskId;
+        mScheduledMoveTime = SystemClock.uptimeMillis();
+    }
+
+
+    public synchronized ArrayList<Task> reorder(ArrayList<Task> tasks) {
         if (!ENABLE_TASK_STABILIZER.get()) {
             return tasks;
         }
 
-        // Create task id array
-        int count = tasks.size();
-        mTempArray.clear();
-        mTempSet.clear();
-        mTempMap.clear();
+        applyScheduledMoveIfTime();
 
-        for (int i = 0; i < count; i++) {
-            Task t = tasks.get(i);
-            mTempMap.put(t.key.id, t);
-
-            mTempSet.add(t.key.id);
-            mTempArray.add(t.key.id);
+        // Ensure that we have enough wrappers
+        int taskCount = tasks.size();
+        for (int i = taskCount - mTaskWrappers.size(); i > 0; i--) {
+            mTaskWrappers.add(new TaskWrapper());
         }
 
-        if (mLastSet.equals(mTempSet) && isStabilizationQuickEnough()) {
-            if (mLastStableOrder.equals(mTempArray)) {
-                // Everything is same
-                return tasks;
-            }
+        List<TaskWrapper> listToSort = mTaskWrappers.size() == taskCount
+                ? mTaskWrappers : mTaskWrappers.subList(0, taskCount);
+        int missingTaskIndex = -taskCount;
 
-            if (!mLastUnstableOrder.equals(mTempArray)) {
-                // Fast reordering, record the current time.
-                mLastUnstableOrder.copyFrom(mTempArray);
-                mLastReorderTime = SystemClock.uptimeMillis();
-            }
+        for (int i = 0; i < taskCount; i++){
+            TaskWrapper wrapper = listToSort.get(i);
+            wrapper.task = tasks.get(i);
+            wrapper.index = mOrderedTaskIds.indexOf(wrapper.task.key.id);
 
-            // Reorder the tasks based on the last stable order.
-            ArrayList<Task> sorted = new ArrayList<>(count);
-            for (int i = 0; i < count; i++) {
-                sorted.add(mTempMap.get(mLastStableOrder.get(i)));
+            // Ensure that missing tasks are put in the front, in the order they appear in the
+            // original list
+            if (wrapper.index < 0) {
+                wrapper.index = missingTaskIndex;
+                missingTaskIndex++;
             }
-            return sorted;
         }
+        Collections.sort(listToSort);
 
-        // Cache the data
-        mLastStableOrder.copyFrom(mTempArray);
-        mLastUnstableOrder.copyFrom(mTempArray);
-        mLastSet.copyFrom(mTempSet);
-
-        mLastReorderTime = SystemClock.uptimeMillis();
-
-        return tasks;
+        ArrayList<Task> result = new ArrayList<>(taskCount);
+        for (int i = 0; i < taskCount; i++) {
+            result.add(listToSort.get(i).task);
+        }
+        return result;
     }
 
-    private boolean isStabilizationQuickEnough() {
-        return (SystemClock.uptimeMillis() - mLastReorderTime) < TASK_CACHE_TIMEOUT_MS;
+    private static class TaskWrapper implements Comparable<TaskWrapper> {
+        Task task;
+        int index;
+
+        @Override
+        public int compareTo(TaskWrapper other) {
+            return Integer.compare(index, other.index);
+        }
     }
 }