Ensuring that the undocked task is visible in Overview.

Bug: 26043233
Change-Id: I2186bdfc64af161b8e828061a8e064f90c5cae24
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 9f0ac35..ec963da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -51,6 +51,7 @@
 import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryEvent;
+import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
 import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -526,6 +527,22 @@
     }
 
     @Override
+    public void onMultiWindowModeChanged(boolean multiWindowMode) {
+        super.onMultiWindowModeChanged(multiWindowMode);
+        if (!multiWindowMode) {
+            RecentsTaskLoader loader = Recents.getTaskLoader();
+            RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+            launchOpts.loadIcons = false;
+            launchOpts.loadThumbnails = false;
+            launchOpts.onlyLoadForCache = true;
+            RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+            loader.preloadTasks(loadPlan, false);
+            loader.loadTasks(this, loadPlan, launchOpts);
+            EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack()));
+        }
+    }
+
+    @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_TAB: {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
new file mode 100644
index 0000000..b94ed7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.RecentsAppWidgetHost;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.TaskStack;
+
+/**
+ * This is sent by the activity whenever the task stach has changed.
+ */
+public class TaskStackUpdatedEvent extends EventBus.Event {
+
+    /**
+     * A new TaskStack instance representing the latest stack state.
+     */
+    public final TaskStack stack;
+
+    public TaskStackUpdatedEvent(TaskStack stack) {
+        this.stack = stack;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index dae6e94..0884695d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -200,7 +200,7 @@
         allTasks.addAll(stackTasks);
         allTasks.addAll(freeformTasks);
         mStack = new TaskStack();
-        mStack.setTasks(allTasks);
+        mStack.setTasks(allTasks, false /* notifyStackChanges */);
         mStack.createAffiliatedGroupings(mContext);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 5d07b10..69a94fe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -206,11 +206,20 @@
 
     /** Task stack callbacks */
     public interface TaskStackCallbacks {
-        /* Notifies when a task has been removed from the stack */
+        /**
+         * Notifies when a new task has been added to the stack.
+         */
+        void onStackTaskAdded(TaskStack stack, Task newTask);
+
+        /**
+         * Notifies when a task has been removed from the stack.
+         */
         void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
             Task newFrontMostTask);
 
-        /* Notifies when a task has been removed from the history */
+        /**
+         * Notifies when a task has been removed from the history.
+         */
         void onHistoryTaskRemoved(TaskStack stack, Task removedTask);
     }
 
@@ -315,6 +324,7 @@
     // The task offset to apply to a task id as a group affiliation
     static final int IndividualTaskIdOffset = 1 << 16;
 
+    ArrayList<Task> mRawTaskList = new ArrayList<>();
     FilteredTaskList mStackTaskList = new FilteredTaskList();
     FilteredTaskList mHistoryTaskList = new FilteredTaskList();
     TaskStackCallbacks mCb;
@@ -430,19 +440,72 @@
 
     /**
      * Sets a few tasks in one go, without calling any callbacks.
+     *
+     * @param tasks the new set of tasks to replace the current set.
+     * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
      */
-    public void setTasks(List<Task> tasks) {
+    public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
+        // Compute a has set for each of the tasks
+        HashMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
+        HashMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
+
+        ArrayList<Task> newTasks = new ArrayList<>();
+
+        // Disable notifications if there are no callbacks
+        if (mCb == null) {
+            notifyStackChanges = false;
+        }
+
+        // Remove any tasks that no longer exist
+        int taskCount = mRawTaskList.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = mRawTaskList.get(i);
+            if (!newTasksMap.containsKey(task.key)) {
+                if (notifyStackChanges) {
+                    mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null);
+                }
+            } else {
+                newTasks.add(task);
+            }
+        }
+
+        // Add any new tasks
+        taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (!currentTasksMap.containsKey(task.key)) {
+                if (notifyStackChanges) {
+                    mCb.onStackTaskAdded(this, task);
+                }
+                newTasks.add(task);
+            } else {
+                newTasks.add(currentTasksMap.get(task.key));
+            }
+        }
+
+        // Sort all the tasks to ensure they are ordered correctly
+        Collections.sort(newTasks, LAST_ACTIVE_TIME_COMPARATOR);
+
+        // TODO: Update screen pinning for the new front-most task post refactoring lockToTask out
+        // of the Task
+
+        // Filter out the historical tasks from this new list
         ArrayList<Task> stackTasks = new ArrayList<>();
         ArrayList<Task> historyTasks = new ArrayList<>();
-        for (Task task : tasks) {
+        int newTaskCount = newTasks.size();
+        for (int i = 0; i < newTaskCount; i++) {
+            Task task = newTasks.get(i);
             if (task.isHistorical) {
                 historyTasks.add(task);
             } else {
                 stackTasks.add(task);
             }
         }
+
         mStackTaskList.set(stackTasks);
         mHistoryTaskList.set(historyTasks);
+        mRawTaskList.clear();
+        mRawTaskList.addAll(newTasks);
     }
 
     /** Gets the front task */
@@ -714,4 +777,17 @@
         }
         return str;
     }
-}
\ No newline at end of file
+
+    /**
+     * Given a list of tasks, returns a map of each task's key to the task.
+     */
+    private HashMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
+        HashMap<Task.TaskKey, Task> map = new HashMap<>();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            map.put(task.key, task);
+        }
+        return map;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 21c08d1..5b6fdd1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -50,6 +50,7 @@
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryEvent;
+import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
@@ -599,6 +600,10 @@
         hideHistoryButton(100);
     }
 
+    public final void onBusEvent(TaskStackUpdatedEvent event) {
+        mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
+    }
+
     /**
      * Shows the history button.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 4e75d5a..4140fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -103,6 +103,8 @@
     private static final float SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
     private static final float HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
 
+    private static final int DEFAULT_SYNC_STACK_DURATION = 200;
+
     public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
             new IntProperty<Drawable>("drawableAlpha") {
                 @Override
@@ -1199,6 +1201,15 @@
     /**** TaskStackCallbacks Implementation ****/
 
     @Override
+    public void onStackTaskAdded(TaskStack stack, Task newTask) {
+        // Update the min/max scroll and animate other task views into their new positions
+        updateLayout(true);
+
+        // Animate all the tasks into place
+        requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
+    }
+
+    @Override
     public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
             Task newFrontMostTask) {
         if (mFocusedTask == removedTask) {
@@ -1244,7 +1255,7 @@
             }
 
             // Animate all the tasks into place
-            requestSynchronizeStackViewsWithModel(200);
+            requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
         } else {
             // Remove the view associated with this task, we can't rely on updateTransforms
             // to work here because the task is no longer in the list
@@ -1257,7 +1268,7 @@
             updateLayout(true);
 
             // Animate all the tasks into place
-            requestSynchronizeStackViewsWithModel(200);
+            requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
         }
 
         // Update the new front most task