Merge changes I5c414d67,I83d74c94 into nyc-dev

* changes:
  Just return the task to the pool if it is not in the current stack.
  Refactoring to update stack in onResume.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index f5ae351..54bf68b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -513,8 +513,9 @@
      * Handle Recents activity visibility changed.
      */
     public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
-        int processUser = event.systemServicesProxy.getProcessUser();
-        if (event.systemServicesProxy.isSystemUser(processUser)) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        int processUser = ssp.getProcessUser();
+        if (ssp.isSystemUser(processUser)) {
             mImpl.onVisibilityChanged(event.applicationContext, event.visible);
         } else {
             postToSystemUser(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index a2934d7..cd1a27f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -55,7 +55,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.MultiWindowStateChangedEvent;
 import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -97,6 +97,7 @@
     private long mLastTabKeyEventTime;
     private boolean mFinishedOnStartup;
     private boolean mIgnoreAltTabRelease;
+    private boolean mIsVisible;
 
     // Top level views
     private RecentsView mRecentsView;
@@ -174,59 +175,6 @@
         }
     };
 
-    /** Updates the set of recent tasks */
-    void updateRecentsTasks() {
-        // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
-        // reconstructing the task stack
-        RecentsTaskLoader loader = Recents.getTaskLoader();
-        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
-        if (plan == null) {
-            plan = loader.createLoadPlan(this);
-        }
-
-        // Start loading tasks according to the load plan
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        if (!plan.hasTasks()) {
-            loader.preloadTasks(plan, -1, launchState.launchedFromHome);
-        }
-        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
-        loadOpts.runningTaskId = launchState.launchedToTaskId;
-        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
-        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
-        loader.loadTasks(this, plan, loadOpts);
-
-        TaskStack stack = plan.getTaskStack();
-        mRecentsView.setTaskStack(stack);
-
-        // Animate the SystemUI scrims into view
-        Task launchTarget = stack.getLaunchTarget();
-        int taskCount = stack.getTaskCount();
-        int launchTaskIndexInStack = launchTarget != null
-                ? stack.indexOfStackTask(launchTarget)
-                : 0;
-        boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
-        boolean animateNavBarScrim = !launchState.launchedWhileDocking;
-        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
-
-        // Keep track of whether we launched from the nav bar button or via alt-tab
-        if (launchState.launchedWithAltTab) {
-            MetricsLogger.count(this, "overview_trigger_alttab", 1);
-        } else {
-            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
-        }
-        // Keep track of whether we launched from an app or from home
-        if (launchState.launchedFromApp) {
-            MetricsLogger.count(this, "overview_source_app", 1);
-            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
-            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
-        } else {
-            MetricsLogger.count(this, "overview_source_home", 1);
-        }
-        // Keep track of the total stack task count
-        MetricsLogger.histogram(this, "overview_task_count", taskCount);
-    }
-
     /**
      * Dismisses the history view back into the stack view.
      */
@@ -345,6 +293,7 @@
 
         // Set the Recents layout
         setContentView(R.layout.recents);
+        takeKeyEvents(true);
         mRecentsView = (RecentsView) findViewById(R.id.recents_view);
         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
@@ -382,45 +331,12 @@
     }
 
     @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        setIntent(intent);
-    }
-
-    @Override
     protected void onStart() {
         super.onStart();
 
-        // Update the recent tasks
-        updateRecentsTasks();
-
-        // If this is a new instance from a configuration change, then we have to manually trigger
-        // the enter animation state, or if recents was relaunched by AM, without going through
-        // the normal mechanisms
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
-                !launchState.launchedFromApp;
-        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
-            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
-        }
-
         // Notify that recents is now visible
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
-
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
         MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
-
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-
-            @Override
-            public boolean onPreDraw() {
-                mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
-                EventBus.getDefault().post(new RecentsDrawnEvent());
-                return true;
-            }
-        });
     }
 
     @Override
@@ -430,26 +346,91 @@
     }
 
     @Override
-    protected void onPause() {
-        super.onPause();
+    protected void onResume() {
+        super.onResume();
 
-        // Stop the fast-toggle dozer
-        mIterateTrigger.stopDozing();
+        // If the Recents component has preloaded a load plan, then use that to prevent
+        // reconstructing the task stack
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+        RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
+        if (loadPlan == null) {
+            loadPlan = loader.createLoadPlan(this);
+        }
+
+        // Start loading tasks according to the load plan
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        if (!loadPlan.hasTasks()) {
+            loader.preloadTasks(loadPlan, -1, launchState.launchedFromHome);
+        }
+
+        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+        loadOpts.runningTaskId = launchState.launchedToTaskId;
+        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+        loader.loadTasks(this, loadPlan, loadOpts);
+        TaskStack stack = loadPlan.getTaskStack();
+        mRecentsView.onResume(mIsVisible, stack);
+
+        // Animate the SystemUI scrims into view
+        Task launchTarget = stack.getLaunchTarget();
+        int taskCount = stack.getTaskCount();
+        int launchTaskIndexInStack = launchTarget != null
+                ? stack.indexOfStackTask(launchTarget)
+                : 0;
+        boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
+        boolean animateNavBarScrim = !launchState.launchedWhileDocking;
+        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
+
+        // If this is a new instance from a configuration change, then we have to manually trigger
+        // the enter animation state, or if recents was relaunched by AM, without going through
+        // the normal mechanisms
+        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+                !launchState.launchedFromApp;
+        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+        }
+
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+
+                    @Override
+                    public boolean onPreDraw() {
+                        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+                        EventBus.getDefault().post(new RecentsDrawnEvent());
+                        return true;
+                    }
+                });
+
+        // Keep track of whether we launched from the nav bar button or via alt-tab
+        if (launchState.launchedWithAltTab) {
+            MetricsLogger.count(this, "overview_trigger_alttab", 1);
+        } else {
+            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
+        }
+
+        // Keep track of whether we launched from an app or from home
+        if (launchState.launchedFromApp) {
+            MetricsLogger.count(this, "overview_source_app", 1);
+            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
+            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
+        } else {
+            MetricsLogger.count(this, "overview_source_home", 1);
+        }
+
+        // Keep track of the total stack task count
+        MetricsLogger.histogram(this, "overview_task_count", taskCount);
+
+        // After we have resumed, set the visible state until the next onStop() call
+        mIsVisible = true;
     }
 
     @Override
-    protected void onStop() {
-        super.onStop();
+    protected void onPause() {
+        super.onPause();
 
-        // Reset some states
         mIgnoreAltTabRelease = false;
-        if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
-            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
-        }
-
-        // Notify that recents is now hidden
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
+        mIterateTrigger.stopDozing();
 
         // Workaround for b/22542869, if the RecentsActivity is started again, but without going
         // through SystemUI, we need to reset the config launch flags to ensure that we do not
@@ -457,7 +438,20 @@
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
         launchState.reset();
+    }
 
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        // Only hide the history if Recents is completely hidden
+        if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
+            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
+        }
+
+        // Notify that recents is now hidden
+        mIsVisible = false;
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
         MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
     }
 
@@ -525,16 +519,22 @@
     public void onMultiWindowChanged(boolean inMultiWindow) {
         super.onMultiWindowChanged(inMultiWindow);
         EventBus.getDefault().send(new ConfigurationChangedEvent());
+
+        // Reload the task stack completely
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
         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, -1, false);
-        loader.loadTasks(this, loadPlan, launchOpts);
-        EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack(),
-                inMultiWindow));
+        loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
+
+        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+        loader.loadTasks(this, loadPlan, loadOpts);
+
+        mRecentsView.onResume(mIsVisible, loadPlan.getTaskStack());
+
+        EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 1458d7b..ac23f44 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -602,7 +602,7 @@
                 com.android.internal.R.dimen.navigation_bar_width);
         mTaskBarHeight = res.getDimensionPixelSize(
                 R.dimen.recents_task_bar_height);
-        mDummyStackView = new TaskStackView(mContext, new TaskStack());
+        mDummyStackView = new TaskStackView(mContext);
         mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
                 null, false);
     }
@@ -615,8 +615,7 @@
      *                               is not already bound (can be expensive)
      * @param stack the stack to initialize the stack layout with
      */
-    private void updateHeaderBarLayout(boolean tryAndBindSearchWidget,
-            TaskStack stack) {
+    private void updateHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
         RecentsConfiguration config = Recents.getConfiguration();
         SystemServicesProxy ssp = Recents.getSystemServices();
         Rect systemInsets = new Rect();
@@ -640,14 +639,15 @@
                 mSearchBarBounds, mTaskStackBounds);
 
         // Rebind the header bar and draw it for the transition
-        TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
+        TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
         Rect taskStackBounds = new Rect(mTaskStackBounds);
-        algo.setSystemInsets(systemInsets);
+        stackLayout.setSystemInsets(systemInsets);
         if (stack != null) {
-            algo.initialize(taskStackBounds,
+            stackLayout.initialize(taskStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
+            mDummyStackView.setTasks(stack, false /* notifyStackChanges */);
         }
-        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+        Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
         if (!taskViewBounds.equals(mLastTaskViewBounds)) {
             mLastTaskViewBounds.set(taskViewBounds);
 
@@ -705,7 +705,6 @@
         updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
 
         // Update the destination rect
-        mDummyStackView.updateLayoutForStack(stack);
         final Task toTask = new Task();
         final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
         ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@@ -887,7 +886,6 @@
         updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
 
         // Prepare the dummy stack for the transition
-        mDummyStackView.updateLayoutForStack(stack);
         TaskStackLayoutAlgorithm.VisibilityReport stackVr =
                 mDummyStackView.computeStackVisibilityReport();
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
new file mode 100644
index 0000000..19245d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.events.EventBus;
+
+/**
+ * This is sent by the activity whenever the multi-window state has changed.
+ */
+public class MultiWindowStateChangedEvent extends EventBus.Event {
+
+    public final boolean inMultiWindow;
+
+    public MultiWindowStateChangedEvent(boolean inMultiWindow) {
+        this.inMultiWindow = inMultiWindow;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
index 4140bcd..8843eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
@@ -19,21 +19,18 @@
 import android.content.Context;
 
 import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 
 /**
- * This is sent when the visibility of the RecentsActivity for the current user changes.
+ * This is sent when the visibility of the RecentsActivity for the current user changes.  Handlers
+ * of this event should not alter the UI, as the activity may still be visible.
  */
 public class RecentsVisibilityChangedEvent extends EventBus.Event {
 
     public final Context applicationContext;
-    public final SystemServicesProxy systemServicesProxy;
     public final boolean visible;
 
-    public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
-            boolean visible) {
+    public RecentsVisibilityChangedEvent(Context context, boolean visible) {
         this.applicationContext = context.getApplicationContext();
-        this.systemServicesProxy = systemServicesProxy;
         this.visible = visible;
     }
 }
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 abcb563..6ae07fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -212,8 +212,7 @@
 
         // Initialize the stacks
         mStack = new TaskStack();
-        mStack.setTasks(allTasks, false /* notifyStackChanges */);
-        mStack.createAffiliatedGroupings(mContext);
+        mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index e5d4f1b..b5a5949 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -100,11 +100,7 @@
 
         @Override
         public String toString() {
-            return "Task.Key: " + id + ", "
-                    + "s: " + stackId + ", "
-                    + "u: " + userId + ", "
-                    + "lat: " + lastActiveTime + ", "
-                    + getComponent().getPackageName();
+            return "t" + id + ", s" + stackId + ", u" + userId;
         }
 
         private void updateHashCode() {
@@ -204,7 +200,9 @@
         this.isDockable = isDockable;
     }
 
-    /** Copies the other task. */
+    /**
+     * Copies the metadata from another task, but retains the current callbacks.
+     */
     public void copyFrom(Task o) {
         this.key = o.key;
         this.group = o.group;
@@ -300,11 +298,6 @@
 
     @Override
     public String toString() {
-        String groupAffiliation = "no group";
-        if (group != null) {
-            groupAffiliation = Integer.toString(group.affiliation);
-        }
-        return "Task (" + groupAffiliation + "): " + key +
-                " [" + super.toString() + "]";
+        return "[" + key.toString() + "] " + title;
     }
 }
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 1f91dce..4d1c552 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -92,15 +92,6 @@
         }
     }
 
-    /**
-     * Resets the task list, but does not remove the filter.
-     */
-    void reset() {
-        mTasks.clear();
-        mFilteredTasks.clear();
-        mTaskIndices.clear();
-    }
-
     /** Removes the task filter and returns the previous touch state */
     void removeFilter() {
         mFilter = null;
@@ -481,15 +472,6 @@
         mCb = cb;
     }
 
-    /** Resets this TaskStack. */
-    public void reset() {
-        mCb = null;
-        mStackTaskList.reset();
-        mHistoryTaskList.reset();
-        mGroups.clear();
-        mAffinitiesGroups.clear();
-    }
-
     /**
      * Moves the given task to either the front of the freeform workspace or the stack.
      */
@@ -556,12 +538,12 @@
      * @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, boolean notifyStackChanges) {
+    public void setTasks(Context context, List<Task> tasks, boolean notifyStackChanges) {
         // Compute a has set for each of the tasks
         ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
         ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
-
-        ArrayList<Task> newTasks = new ArrayList<>();
+        ArrayList<Task> addedTasks = new ArrayList<>();
+        ArrayList<Task> allTasks = new ArrayList<>();
 
         // Disable notifications if there are no callbacks
         if (mCb == null) {
@@ -570,10 +552,13 @@
 
         // Remove any tasks that no longer exist
         int taskCount = mRawTaskList.size();
-        for (int i = 0; i < taskCount; i++) {
+        for (int i = taskCount - 1; i >= 0; i--) {
             Task task = mRawTaskList.get(i);
             if (!newTasksMap.containsKey(task.key)) {
                 if (notifyStackChanges) {
+                    // If we are notifying, then remove the task now, otherwise the raw task list
+                    // will be reset at the end of this method
+                    removeTask(task, AnimationProps.IMMEDIATE);
                     mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null,
                             AnimationProps.IMMEDIATE);
                 }
@@ -584,26 +569,28 @@
         // 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));
+            Task newTask = tasks.get(i);
+            Task currentTask = currentTasksMap.get(newTask.key);
+            if (currentTask == null && notifyStackChanges) {
+                addedTasks.add(newTask);
+            } else if (currentTask != null) {
+                // The current task has bound callbacks, so just copy the data from the new task
+                // state and add it back into the list
+                currentTask.copyFrom(newTask);
+                newTask = currentTask;
             }
+            allTasks.add(newTask);
         }
 
         // Sort all the tasks to ensure they are ordered correctly
-        Collections.sort(newTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
+        Collections.sort(allTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
 
         // Filter out the historical tasks from this new list
         ArrayList<Task> stackTasks = new ArrayList<>();
         ArrayList<Task> historyTasks = new ArrayList<>();
-        int newTaskCount = newTasks.size();
+        int newTaskCount = allTasks.size();
         for (int i = 0; i < newTaskCount; i++) {
-            Task task = newTasks.get(i);
+            Task task = allTasks.get(i);
             if (task.isHistorical) {
                 historyTasks.add(task);
             } else {
@@ -613,10 +600,16 @@
 
         mStackTaskList.set(stackTasks);
         mHistoryTaskList.set(historyTasks);
-        mRawTaskList.clear();
-        mRawTaskList.addAll(newTasks);
-        mGroups.clear();
-        mAffinitiesGroups.clear();
+        mRawTaskList = allTasks;
+
+        // Only callback for the newly added tasks after this stack has been updated
+        int addedTaskCount = addedTasks.size();
+        for (int i = 0; i < addedTaskCount; i++) {
+            mCb.onStackTaskAdded(this, addedTasks.get(i));
+        }
+
+        // Update the affiliated groupings
+        createAffiliatedGroupings(context);
     }
 
     /**
@@ -779,9 +772,12 @@
     }
 
     /**
-     * Temporary: This method will simulate affiliation groups by
+     * Temporary: This method will simulate affiliation groups
      */
-    public void createAffiliatedGroupings(Context context) {
+    void createAffiliatedGroupings(Context context) {
+        mGroups.clear();
+        mAffinitiesGroups.clear();
+
         if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
             ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
             // Sort all tasks by increasing firstActiveTime of the task
@@ -926,13 +922,13 @@
 
     @Override
     public String toString() {
-        String str = "Stack Tasks:\n";
+        String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
         for (Task t : mStackTaskList.getTasks()) {
-            str += "  " + t.toString() + "\n";
+            str += "    " + t.toString() + "\n";
         }
-        str += "Historical Tasks:\n";
+        str += "Historical Tasks(" + mHistoryTaskList.size() + "):\n";
         for (Task t : mHistoryTaskList.getTasks()) {
-            str += "  " + t.toString() + "\n";
+            str += "    " + t.toString() + "\n";
         }
         return str;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 02c8d96..dae522f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -294,7 +294,7 @@
 
         // Notify that recents is now visible
         SystemServicesProxy ssp = Recents.getSystemServices();
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
 
         if (mPipManager.isPipShown()) {
             // Place mPipView at the PIP bounds for fine tuned focus handling.
@@ -343,8 +343,7 @@
         mPipManager.removeListener(mPipListener);
         mIgnoreAltTabRelease = false;
         // Notify that recents is now hidden
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
 
         // Workaround for b/22542869, if the RecentsActivity is started again, but without going
         // through SystemUI, we need to reset the config launch flags to ensure that we do not
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index e5022a4..dd825cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -177,6 +177,7 @@
             // Keep track of failed launches
             EventBus.getDefault().send(new LaunchTaskFailedEvent());
         }
+
         if (transitionFuture != null) {
             IRemoteCallback.Stub callback = null;
             if (animStartedListener != null) {
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 10f491e..1af5a55 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -60,9 +60,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.activity.ToggleHistoryEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
 import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
@@ -168,35 +166,39 @@
     }
 
     /** Set/get the bsp root node */
-    public void setTaskStack(TaskStack stack) {
+    public void onResume(boolean isResumingFromVisible, TaskStack stack) {
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
-        mStack = stack;
-        if (launchState.launchedReuseTaskStackViews) {
-            if (mTaskStackView != null) {
-                // If onRecentsHidden is not triggered, we need to the stack view again here
-                mTaskStackView.reset();
-                mTaskStackView.setStack(stack);
-            } else {
-                mTaskStackView = new TaskStackView(getContext(), stack);
-                addView(mTaskStackView);
-            }
-        } else {
-            if (mTaskStackView != null) {
-                removeView(mTaskStackView);
-            }
-            mTaskStackView = new TaskStackView(getContext(), stack);
+
+        if (mTaskStackView == null || !launchState.launchedReuseTaskStackViews) {
+            isResumingFromVisible = false;
+            removeView(mTaskStackView);
+            mTaskStackView = new TaskStackView(getContext());
+            mStack = mTaskStackView.getStack();
             addView(mTaskStackView);
         }
 
-        // If we are already occluded by the app, then just set the default background scrim now.
-        // Otherwise, defer until the enter animation completes to animate the scrim with the
-        // tasks for the home animation.
-        if (launchState.launchedWhileDocking || launchState.launchedFromApp
-                || mStack.getTaskCount() == 0) {
-            mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
+        // Reset the state
+        mAwaitingFirstLayout = !isResumingFromVisible;
+        mLastTaskLaunchedWasFreeform = false;
+
+        // Update the stack
+        mTaskStackView.onResume(isResumingFromVisible);
+        mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */);
+
+        if (isResumingFromVisible) {
+            // If we are already visible, then restore the background scrim
+            animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
         } else {
-            mBackgroundScrim.setAlpha(0);
+            // If we are already occluded by the app, then set the final background scrim alpha now.
+            // Otherwise, defer until the enter animation completes to animate the scrim alpha with
+            // the tasks for the home animation.
+            if (launchState.launchedWhileDocking || launchState.launchedFromApp
+                    || mStack.getTaskCount() == 0) {
+                mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
+            } else {
+                mBackgroundScrim.setAlpha(0);
+            }
         }
 
         // Update the top level view's visibilities
@@ -205,9 +207,6 @@
         } else {
             showEmptyView();
         }
-
-        // Trigger a new layout
-        requestLayout();
     }
 
     /**
@@ -662,13 +661,6 @@
         animator.start();
     }
 
-    public final void onBusEvent(TaskStackUpdatedEvent event) {
-        if (!event.inMultiWindow) {
-            mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
-            mStack.createAffiliatedGroupings(getContext());
-        }
-    }
-
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         if (!launchState.launchedWhileDocking && !launchState.launchedFromApp
@@ -686,17 +678,6 @@
         animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
     }
 
-    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
-        if (!event.visible) {
-            // Reset the view state
-            mAwaitingFirstLayout = true;
-            mLastTaskLaunchedWasFreeform = false;
-            if (RecentsDebugFlags.Static.EnableHistory) {
-                hideHistoryButton(0, false /* translate */);
-            }
-        }
-    }
-
     public final void onBusEvent(ToggleHistoryEvent event) {
         if (!RecentsDebugFlags.Static.EnableHistory) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 1cd0850..19b219a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -16,9 +16,11 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.AnimatorListenerAdapter;
 import android.app.Activity;
 import android.content.Context;
 import android.view.View;
+import android.view.ViewPropertyAnimator;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -56,25 +58,41 @@
                 View.VISIBLE : View.INVISIBLE);
     }
 
+    /**
+     * Animates the nav bar scrim visibility.
+     */
+    public void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
+        int toY = 0;
+        if (visible) {
+            mNavBarScrimView.setVisibility(View.VISIBLE);
+            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+        } else {
+            toY = mNavBarScrimView.getMeasuredHeight();
+        }
+        if (animation != AnimationProps.IMMEDIATE) {
+            mNavBarScrimView.animate()
+                    .translationY(toY)
+                    .setDuration(animation.getDuration(AnimationProps.BOUNDS))
+                    .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
+                    .start();
+        } else {
+            mNavBarScrimView.setTranslationY(toY);
+        }
+    }
+
     /**** EventBus events ****/
 
     /**
      * Starts animating the scrim views when entering Recents.
      */
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
-        if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
-            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
-            mNavBarScrimView.animate()
-                    .translationY(0)
-                    .setDuration(mNavBarScrimEnterDuration)
-                    .setInterpolator(Interpolators.DECELERATE_QUINT)
-                    .withStartAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mNavBarScrimView.setVisibility(View.VISIBLE);
-                        }
-                    })
-                    .start();
+        if (mHasNavBarScrim) {
+            AnimationProps animation = mShouldAnimateNavBarScrim
+                    ? new AnimationProps()
+                            .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
+                            .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
+                    : AnimationProps.IMMEDIATE;
+            animateNavBarScrimVisibility(true, animation);
         }
     }
 
@@ -83,13 +101,12 @@
      * going home).
      */
     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
-        if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
-            mNavBarScrimView.animate()
-                    .translationY(mNavBarScrimView.getMeasuredHeight())
-                    .setStartDelay(0)
-                    .setDuration(TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .start();
+        if (mHasNavBarScrim) {
+            AnimationProps animation = new AnimationProps()
+                    .setDuration(AnimationProps.BOUNDS,
+                            TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
+                    .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+            animateNavBarScrimVisibility(false, animation);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 17c3eeb..699b85e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -531,7 +531,6 @@
             return;
         }
 
-        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         mUnfocusedRange.offset(0f);
         int taskCount = tasks.size();
         for (int i = taskCount - 1; i >= 0; i--) {
@@ -571,6 +570,10 @@
      * Updates this stack when a scroll happens.
      */
     public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
+        if (deltaScroll == 0f) {
+            return;
+        }
+
         for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
             int taskId = mTaskIndexOverrideMap.keyAt(i);
             float x = mTaskIndexMap.get(taskId);
@@ -631,6 +634,13 @@
     }
 
     /**
+     * Returns whether this stack layout has been initialized.
+     */
+    public boolean isInitialized() {
+        return !mStackRect.isEmpty();
+    }
+
+    /**
      * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
      * stack scroll.  Requires that update() is called first.
      */
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 c2bfc28..6abb826 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -65,11 +65,10 @@
 import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 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.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
@@ -117,7 +116,7 @@
     private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
 
     LayoutInflater mInflater;
-    TaskStack mStack;
+    TaskStack mStack = new TaskStack();
     @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
     TaskStackLayoutAlgorithm mLayoutAlgorithm;
     @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
@@ -206,13 +205,13 @@
         }
     };
 
-    public TaskStackView(Context context, TaskStack stack) {
+    public TaskStackView(Context context) {
         super(context);
         SystemServicesProxy ssp = Recents.getSystemServices();
         Resources res = context.getResources();
 
         // Set the stack first
-        setStack(stack);
+        mStack.setCallbacks(this);
         mViewPool = new ViewPool<>(context, this);
         mInflater = LayoutInflater.from(context);
         mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
@@ -248,6 +247,41 @@
         }
     }
 
+    /**
+     * Called only if we are resuming Recents.
+     */
+    void onResume(boolean isResumingFromVisible) {
+        if (!isResumingFromVisible) {
+            // Reset the focused task
+            resetFocusedTask(getFocusedTask());
+        }
+
+        // Reset the state of each of the task views
+        List<TaskView> taskViews = new ArrayList<>();
+        taskViews.addAll(getTaskViews());
+        taskViews.addAll(mViewPool.getViews());
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            taskViews.get(i).onResume(isResumingFromVisible);
+        }
+
+        // Reset the stack state
+        readSystemFlags();
+        mTaskViewsClipDirty = true;
+        mEnterAnimationComplete = false;
+        mUIDozeTrigger.stopDozing();
+        if (isResumingFromVisible) {
+            // Animate in the freeform workspace
+            int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
+            animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
+                    Interpolators.FAST_OUT_SLOW_IN));
+        } else {
+            mStackScroller.reset();
+            mLayoutAlgorithm.reset();
+            mAwaitingFirstLayout = true;
+            requestLayout();
+        }
+    }
+
     @Override
     protected void onAttachedToWindow() {
         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -261,15 +295,20 @@
         EventBus.getDefault().unregister(this);
     }
 
-    /** Sets the task stack */
-    void setStack(TaskStack stack) {
-        // Set the new stack
-        mStack = stack;
-        if (mStack != null) {
-            mStack.setCallbacks(this);
+    /**
+     * Sets the stack tasks of this TaskStackView from the given TaskStack.
+     */
+    public void setTasks(TaskStack stack, boolean notifyStackChanges) {
+        boolean isInitialized = mLayoutAlgorithm.isInitialized();
+        mStack.setTasks(getContext(), stack.computeAllTasksList(),
+                notifyStackChanges && isInitialized);
+        if (isInitialized) {
+            // Only update the layout if we are notifying, otherwise, we will update it in the next
+            // measure/layout pass
+            updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
+            updateToInitialState();
+            relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
         }
-        // Layout again with the new stack
-        requestLayout();
     }
 
     /** Returns the task stack. */
@@ -338,36 +377,6 @@
         return null;
     }
 
-    /** Resets this TaskStackView for reuse. */
-    void reset() {
-        // Reset the focused task
-        resetFocusedTask(getFocusedTask());
-
-        // Return all the views to the pool
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            mViewPool.returnViewToPool(taskViews.get(i));
-        }
-
-        // Mark each task view for relayout
-        List<TaskView> poolViews = mViewPool.getViews();
-        for (TaskView tv : poolViews) {
-            tv.reset();
-        }
-
-        // Reset the stack state
-        mStack.reset();
-        mTaskViewsClipDirty = true;
-        mAwaitingFirstLayout = true;
-        mEnterAnimationComplete = false;
-        mUIDozeTrigger.stopDozing();
-        mStackScroller.reset();
-        mLayoutAlgorithm.reset();
-        readSystemFlags();
-        requestLayout();
-    }
-
     /** Returns the stack algorithm for this task stack. */
     public TaskStackLayoutAlgorithm getStackAlgorithm() {
         return mLayoutAlgorithm;
@@ -514,15 +523,22 @@
         for (int i = taskViewCount - 1; i >= 0; i--) {
             TaskView tv = taskViews.get(i);
             Task task = tv.getTask();
-            int taskIndex = mStack.indexOfStackTask(task);
-            TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
 
             // Skip ignored tasks
             if (ignoreTasksSet.contains(task.key)) {
                 continue;
             }
 
-            if (task.isFreeformTask() || transform.visible) {
+            // It is possible for the set of lingering TaskViews to differ from the stack if the
+            // stack was updated before the relayout.  If the task view is no longer in the stack,
+            // then just return it back to the view pool.
+            int taskIndex = mStack.indexOfStackTask(task);
+            TaskViewTransform transform = null;
+            if (taskIndex != -1) {
+                transform = mCurrentTaskTransforms.get(taskIndex);
+            }
+
+            if (task.isFreeformTask() || (transform != null && transform.visible)) {
                 mTmpTaskViewMap.put(task.key, tv);
             } else {
                 if (mTouchExplorationEnabled) {
@@ -1120,15 +1136,6 @@
     }
 
     /**
-     * This is ONLY used from the Recents component to update the dummy stack view for purposes
-     * of getting the task rect to animate to.
-     */
-    public void updateLayoutForStack(TaskStack stack) {
-        mStack = stack;
-        updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
-    }
-
-    /**
      * Computes the maximum number of visible tasks and thumbnails. Requires that
      * updateLayoutForStack() is called first.
      */
@@ -1423,7 +1430,7 @@
     public void onReturnViewToPool(TaskView tv) {
         final Task task = tv.getTask();
 
-        // Report that this tasks's data is no longer being used
+        // Report that this task's data is no longer being used
         Recents.getTaskLoader().unloadTaskData(task);
 
         // Reset the view properties and view state
@@ -1670,12 +1677,6 @@
         }
     }
 
-    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
-        if (!event.visible) {
-            reset();
-        }
-    }
-
     public final void onBusEvent(DragStartEvent event) {
         // Ensure that the drag task is not animated
         addIgnoreTask(event.task);
@@ -1772,12 +1773,14 @@
         event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
                 taskViewRect.right, taskViewRect.bottom);
 
-        // Animate all the TaskViews back into position
+        // Animate the non-drag TaskViews back into position
         mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
                 mTmpTransform, null);
         event.getAnimationTrigger().increment();
         relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
                 Interpolators.FAST_OUT_SLOW_IN));
+
+        // Animate the drag TaskView back into position
         updateTaskViewToTransform(event.taskView, mTmpTransform,
                 new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
                         event.getAnimationTrigger().decrementOnAnimationEnd()));
@@ -1850,31 +1853,22 @@
         mAnimationHelper.startHideHistoryAnimation();
     }
 
-    public final void onBusEvent(TaskStackUpdatedEvent event) {
+    public final void onBusEvent(MultiWindowStateChangedEvent event) {
         if (!event.inMultiWindow) {
-            // Scroll the stack to the front after it has been updated
-            event.addPostAnimationCallback(new Runnable() {
+            // Scroll the stack to the front to see the undocked task
+            mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
                 @Override
                 public void run() {
-                    mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
-                            null /* postScrollRunnable */);
+                    List<TaskView> taskViews = getTaskViews();
+                    int taskViewCount = taskViews.size();
+                    for (int i = 0; i < taskViewCount; i++) {
+                        TaskView tv = taskViews.get(i);
+                        tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
+                                tv.mIsDisabledInSafeMode);
+                    }
                 }
             });
         }
-        // When the multi-window state changes, rebind all task view headers again to update their
-        // dockable state
-        event.addPostAnimationCallback(new Runnable() {
-            @Override
-            public void run() {
-                List<TaskView> taskViews = getTaskViews();
-                int taskViewCount = taskViews.size();
-                for (int i = 0; i < taskViewCount; i++) {
-                    TaskView tv = taskViews.get(i);
-                    tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
-                            tv.mIsDisabledInSafeMode);
-                }
-            }
-        });
     }
 
     public final void onBusEvent(ConfigurationChangedEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index e9c7ac6..0bc7f89 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -193,11 +193,13 @@
     }
 
     /** Resets this TaskView for reuse. */
-    void reset() {
-        resetViewProperties();
+    void onResume(boolean isResumingFromVisible) {
         resetNoUserInteractionState();
         readSystemFlags();
-        setClipViewInStack(false);
+        if (!isResumingFromVisible) {
+            resetViewProperties();
+            setClipViewInStack(false);
+        }
         setCallbacks(null);
     }