Merge changes Iac20cc7b,I87ce6320

* changes:
  Fixing potential issue with wrong task descriptions being loaded.
  Refactoring to AnimatedEvent.
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index b8caf23..04f18c5 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -20,7 +20,7 @@
     android:layout_height="@dimen/recents_task_bar_height"
     android:layout_gravity="top|center_horizontal">
     <com.android.systemui.recents.views.FixedSizeImageView
-        android:id="@+id/application_icon"
+        android:id="@+id/icon"
         android:contentDescription="@string/recents_app_info_button_label"
         android:layout_width="@dimen/recents_task_view_application_icon_size"
         android:layout_height="@dimen/recents_task_view_application_icon_size"
@@ -29,7 +29,7 @@
         android:padding="8dp"
         android:background="@drawable/recents_button_bg" />
     <TextView
-        android:id="@+id/activity_description"
+        android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b3ce4a6..ffcc805 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -246,10 +246,7 @@
     boolean dismissHistory() {
         // Try and hide the history view first
         if (mHistoryView != null && mHistoryView.isVisible()) {
-            ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
-            t.increment();
-            EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
-            t.decrement();
+            EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
             return true;
         }
         return false;
@@ -301,8 +298,8 @@
      */
     void dismissRecentsToHome(boolean animated) {
         if (animated) {
-            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                    null, mFinishLaunchHomeRunnable, null);
+            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(null,
+                    mFinishLaunchHomeRunnable, null);
             mRecentsView.startExitToHomeAnimation(
                     new ViewAnimation.TaskViewExitContext(exitTrigger));
         } else {
@@ -439,10 +436,7 @@
         // Reset some states
         mIgnoreAltTabRelease = false;
         if (mHistoryView != null) {
-            ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
-            t.increment();
-            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */, t));
-            t.decrement();
+            EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
         }
 
         // Notify that recents is now hidden
@@ -511,11 +505,7 @@
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
         if (savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
-            ReferenceCountedTrigger postHideStackAnimationTrigger =
-                    new ReferenceCountedTrigger(this);
-            postHideStackAnimationTrigger.increment();
-            EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
-            postHideStackAnimationTrigger.decrement();
+            EventBus.getDefault().send(new ShowHistoryEvent());
         }
     }
 
@@ -644,16 +634,14 @@
         } else if (event.triggeredFromHomeKey) {
             // Otherwise, dismiss Recents to Home
             if (mHistoryView != null && mHistoryView.isVisible()) {
-                ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
-                t.increment();
-                t.addLastDecrementRunnable(new Runnable() {
+                HideHistoryEvent hideEvent = new HideHistoryEvent(true /* animate */);
+                hideEvent.addPostAnimationCallback(new Runnable() {
                     @Override
                     public void run() {
                         dismissRecentsToHome(true /* animated */);
                     }
                 });
-                EventBus.getDefault().send(new HideHistoryEvent(true, t));
-                t.decrement();
+                EventBus.getDefault().send(hideEvent);
 
             } else {
                 dismissRecentsToHome(true /* animated */);
@@ -665,7 +653,7 @@
 
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
         // Try and start the enter animation (or restart it on configuration changed)
-        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
+        ReferenceCountedTrigger t = new ReferenceCountedTrigger();
         ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
         ctx.postAnimationTrigger.increment();
         if (mSearchWidgetInfo != null) {
@@ -784,11 +772,11 @@
             // provided.
             mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
         }
-        mHistoryView.show(mRecentsView.getTaskStack(), event.postHideStackAnimationTrigger);
+        mHistoryView.show(mRecentsView.getTaskStack(), event.getAnimationTrigger());
     }
 
     public final void onBusEvent(HideHistoryEvent event) {
-        mHistoryView.hide(event.animate, event.postHideHistoryAnimationTrigger);
+        mHistoryView.hide(event.animate, event.getAnimationTrigger());
     }
 
     private void refreshSearchWidgetView() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8a0a043..949fb86 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -207,7 +207,7 @@
         RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
         loader.preloadTasks(plan, true /* isTopTaskHome */);
         RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+        launchOpts.numVisibleTasks = loader.getIconCacheSize();
         launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
         launchOpts.onlyLoadForCache = true;
         loader.loadTasks(mContext, plan, launchOpts);
@@ -452,7 +452,7 @@
         }
 
         // Launch the task
-        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
     }
 
     /**
@@ -524,7 +524,7 @@
         MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
 
         // Launch the task
-        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+        ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
     }
 
     public void showNextAffiliatedTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index d72218f..5c49ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -28,6 +28,8 @@
 import android.util.Log;
 import android.util.MutableBoolean;
 
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -220,6 +222,20 @@
         // Only accessible from derived events
         protected Event() {}
 
+        /**
+         * Called by the EventBus prior to dispatching this event to any subscriber of this event.
+         */
+        void onPreDispatch() {
+            // Do nothing
+        }
+
+        /**
+         * Called by the EventBus after dispatching this event to every subscriber of this event.
+         */
+        void onPostDispatch() {
+            // Do nothing
+        }
+
         @Override
         protected Object clone() throws CloneNotSupportedException {
             Event evt = (Event) super.clone();
@@ -230,6 +246,51 @@
     }
 
     /**
+     * An event that represents an animated state change, which allows subscribers to coordinate
+     * callbacks which happen after the animation has taken place.
+     *
+     * Internally, it is guaranteed that increment() and decrement() will be called before and the
+     * after the event is dispatched.
+     */
+    public static class AnimatedEvent extends Event {
+
+        private final ReferenceCountedTrigger mTrigger = new ReferenceCountedTrigger();
+
+        // Only accessible from derived events
+        protected AnimatedEvent() {}
+
+        /**
+         * Returns the reference counted trigger that coordinates the animations for this event.
+         */
+        public ReferenceCountedTrigger getAnimationTrigger() {
+            return mTrigger;
+        }
+
+        /**
+         * Adds a callback that is guaranteed to be called after the state has changed regardless of
+         * whether an actual animation took place.
+         */
+        public void addPostAnimationCallback(Runnable r) {
+            mTrigger.addLastDecrementRunnable(r);
+        }
+
+        @Override
+        void onPreDispatch() {
+            mTrigger.increment();
+        }
+
+        @Override
+        void onPostDispatch() {
+            mTrigger.decrement();
+        }
+
+        @Override
+        protected Object clone() throws CloneNotSupportedException {
+            throw new CloneNotSupportedException();
+        }
+    }
+
+    /**
      * An inter-process event super class that allows us to track user state across subscriber
      * invocations.
      */
@@ -706,6 +767,11 @@
         if (eventHandlers == null) {
             return;
         }
+
+        // Prepare this event
+        boolean hasPostedEvent = false;
+        event.onPreDispatch();
+
         // We need to clone the list in case a subscriber unregisters itself during traversal
         eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
         for (final EventHandler eventHandler : eventHandlers) {
@@ -717,11 +783,24 @@
                             processEvent(eventHandler, event);
                         }
                     });
+                    hasPostedEvent = true;
                 } else {
                     processEvent(eventHandler, event);
                 }
             }
         }
+
+        // Clean up after this event, deferring until all subscribers have been called
+        if (hasPostedEvent) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    event.onPostDispatch();
+                }
+            });
+        } else {
+            event.onPostDispatch();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
index 3412852..e85dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
@@ -22,17 +22,11 @@
 /**
  * This is sent when the history view will be closed.
  */
-public class HideHistoryEvent extends EventBus.Event {
+public class HideHistoryEvent extends EventBus.AnimatedEvent {
 
     public final boolean animate;
-    public final ReferenceCountedTrigger postHideHistoryAnimationTrigger;
 
-    /**
-     * @param postHideHistoryAnimationTrigger the trigger that gets called when all the history animations are finished
-     *                                        when transitioning from the history view
-     */
-    public HideHistoryEvent(boolean animate, ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
+    public HideHistoryEvent(boolean animate) {
         this.animate = animate;
-        this.postHideHistoryAnimationTrigger = postHideHistoryAnimationTrigger;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
index c91752e..94e5a97 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
@@ -22,15 +22,8 @@
 /**
  * This is sent when the history view button is clicked.
  */
-public class ShowHistoryEvent extends EventBus.Event {
+public class ShowHistoryEvent extends EventBus.AnimatedEvent {
 
-    public final ReferenceCountedTrigger postHideStackAnimationTrigger;
+    // Simple event
 
-    /**
-     * @param postHideStackAnimationTrigger the trigger that gets called when all the task animations are finished when
-     *                                      transitioning to the history view
-     */
-    public ShowHistoryEvent(ReferenceCountedTrigger postHideStackAnimationTrigger) {
-        this.postHideStackAnimationTrigger = postHideStackAnimationTrigger;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 3deeb47..8aa4631 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -25,18 +25,15 @@
 /**
  * This event is sent whenever a drag ends.
  */
-public class DragEndEvent extends EventBus.Event {
+public class DragEndEvent extends EventBus.AnimatedEvent {
 
     public final Task task;
     public final TaskView taskView;
     public final DropTarget dropTarget;
-    public final ReferenceCountedTrigger postAnimationTrigger;
 
-    public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget,
-            ReferenceCountedTrigger postAnimationTrigger) {
+    public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget) {
         this.task = task;
         this.taskView = taskView;
         this.dropTarget = dropTarget;
-        this.postAnimationTrigger = postAnimationTrigger;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 76439c0..72ec7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -31,7 +31,6 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -72,7 +71,7 @@
         public void onTaskDataLoaded(Task task) {
             // This callback is only made for TaskRow view holders
             ImageView iv = (ImageView) content.findViewById(R.id.icon);
-            iv.setImageDrawable(task.applicationIcon);
+            iv.setImageDrawable(task.icon);
         }
 
         @Override
@@ -128,7 +127,7 @@
         @Override
         public void onClick(View v) {
             SystemServicesProxy ssp = Recents.getSystemServices();
-            ssp.startActivityFromRecents(v.getContext(), task.key.id, task.activityLabel,
+            ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
                     ActivityOptions.makeBasic());
         }
 
@@ -240,7 +239,7 @@
                 TaskRow taskRow = (TaskRow) row;
                 taskRow.task.addCallback(holder);
                 TextView tv = (TextView) holder.content.findViewById(R.id.description);
-                tv.setText(taskRow.task.activityLabel);
+                tv.setText(taskRow.task.title);
                 holder.content.setOnClickListener(taskRow);
                 loader.loadTaskData(taskRow.task);
                 break;
@@ -313,10 +312,7 @@
      * Dismisses history back to the stack view.
      */
     private void dismissHistory() {
-        ReferenceCountedTrigger t = new ReferenceCountedTrigger(mContext);
-        t.increment();
-        EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
-        t.decrement();
+        EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
         EventBus.getDefault().send(new HideHistoryButtonEvent());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index b06539a..367f2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -29,9 +29,6 @@
  */
 public class ReferenceCountedTrigger {
 
-    private static final String TAG = "ReferenceCountedTrigger";
-
-    Context mContext;
     int mCount;
     ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
     ArrayList<Runnable> mLastDecRunnables = new ArrayList<Runnable>();
@@ -51,13 +48,12 @@
         }
     };
 
-    public ReferenceCountedTrigger(Context context) {
-        this(context, null, null, null);
+    public ReferenceCountedTrigger() {
+        this(null, null, null);
     }
 
-    public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
-                                   Runnable lastDecRunnable, Runnable errorRunanable) {
-        mContext = context;
+    public ReferenceCountedTrigger(Runnable firstIncRunnable, Runnable lastDecRunnable,
+            Runnable errorRunanable) {
         if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
         if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
         mErrorRunnable = errorRunanable;
@@ -81,22 +77,14 @@
 
     /** Adds a runnable to the last-decrement runnables list. */
     public void addLastDecrementRunnable(Runnable r) {
-        // To ensure that the last decrement always calls, we increment and decrement after setting
-        // the last decrement runnable
-        boolean ensureLastDecrement = (mCount == 0);
-        if (ensureLastDecrement) increment();
         mLastDecRunnables.add(r);
-        if (ensureLastDecrement) decrement();
     }
 
     /** Decrements the ref count */
     public void decrement() {
         mCount--;
-        if (mCount == 0 && !mLastDecRunnables.isEmpty()) {
-            int numRunnables = mLastDecRunnables.size();
-            for (int i = 0; i < numRunnables; i++) {
-                mLastDecRunnables.get(i).run();
-            }
+        if (mCount == 0) {
+            flushLastDecrementRunnables();
         } else if (mCount < 0) {
             if (mErrorRunnable != null) {
                 mErrorRunnable.run();
@@ -106,6 +94,19 @@
         }
     }
 
+    /**
+     * Runs and clears all the last-decrement runnables now.
+     */
+    public void flushLastDecrementRunnables() {
+        if (!mLastDecRunnables.isEmpty()) {
+            int numRunnables = mLastDecRunnables.size();
+            for (int i = 0; i < numRunnables; i++) {
+                mLastDecRunnables.get(i).run();
+            }
+        }
+        mLastDecRunnables.clear();
+    }
+
     /** Convenience method to decrement this trigger as a runnable. */
     public Runnable decrementAsRunnable() {
         return mDecrementRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 5888b30..6a9268a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -43,6 +43,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -572,7 +573,7 @@
      * Returns the activity icon for the ActivityInfo for a user, badging if
      * necessary.
      */
-    public Drawable getActivityIcon(ActivityInfo info, int userId) {
+    public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
         if (mPm == null) return null;
 
         // If we are mocking, then return a mock label
@@ -585,9 +586,31 @@
     }
 
     /**
+     * Returns the task description icon, loading and badging it if it necessary.
+     */
+    public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
+            int userId, Resources res) {
+
+        // If we are mocking, then return a mock label
+        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+            return new ColorDrawable(0xFF666666);
+        }
+
+        Bitmap tdIcon = taskDescription.getInMemoryIcon();
+        if (tdIcon == null) {
+            tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+                    taskDescription.getIconFilename(), userId);
+        }
+        if (tdIcon != null) {
+            return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
+        }
+        return null;
+    }
+
+    /**
      * Returns the given icon for a user, badging if necessary.
      */
-    public Drawable getBadgedIcon(Drawable icon, int userId) {
+    private Drawable getBadgedIcon(Drawable icon, int userId) {
         if (userId != UserHandle.myUserId()) {
             icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
         }
@@ -597,7 +620,7 @@
     /**
      * Returns the given label for a user, badging if necessary.
      */
-    public String getBadgedLabel(String label, int userId) {
+    private String getBadgedLabel(String label, int userId) {
         if (userId != UserHandle.myUserId()) {
             label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
         }
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 1d18087..7a92b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -29,7 +29,6 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.ArrayList;
@@ -47,9 +46,6 @@
  */
 public class RecentsTaskLoadPlan {
 
-    private static String TAG = "RecentsTaskLoadPlan";
-    private static boolean DEBUG = false;
-
     private static int MIN_NUM_TASKS = 5;
     private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
             6 /* hrs */;
@@ -107,13 +103,6 @@
 
         // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
         Collections.reverse(mRawTasks);
-
-        if (DEBUG) {
-            Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
-            for (ActivityManager.RecentTaskInfo info : mRawTasks) {
-                Log.d(TAG, "  " + info.baseIntent + ", " + info.lastActiveTime);
-            }
-        }
     }
 
     /**
@@ -126,8 +115,6 @@
      * - least-recent to most-recent freeform tasks
      */
     public synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
-        if (DEBUG) Log.d(TAG, "preloadPlan");
-
         RecentsConfiguration config = Recents.getConfiguration();
         SystemServicesProxy ssp = Recents.getSystemServices();
         Resources res = mContext.getResources();
@@ -149,38 +136,26 @@
 
             // This task is only shown in the stack if it statisfies the historical time or min
             // number of tasks constraints. Freeform tasks are also always shown.
-            boolean isStackTask = true;
             boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
-            isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
-                    (t.lastActiveTime >= lastStackActiveTime &&
-                            i >= (taskCount - MIN_NUM_TASKS)));
+            boolean isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
+                    (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS)));
             if (isStackTask && newLastStackActiveTime < 0) {
                 newLastStackActiveTime = t.lastActiveTime;
             }
 
-            // Load the label, icon, and color
-            String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
-                    ssp);
-            String contentDescription = loader.getAndUpdateContentDescription(taskKey,
-                    activityLabel, ssp, res);
-            Drawable activityIcon = isStackTask
-                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, ssp, res, false)
+            // Load the title, icon, and color
+            String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
+            String contentDescription = loader.getAndUpdateContentDescription(taskKey, title, res);
+            Drawable icon = isStackTask
+                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                     : null;
+            Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
             int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
 
-            Bitmap icon = t.taskDescription != null
-                    ? t.taskDescription.getInMemoryIcon() : null;
-            String iconFilename = t.taskDescription != null
-                    ? t.taskDescription.getIconFilename() : null;
-
             // Add the task to the stack
-            Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel,
-                    contentDescription, activityIcon, activityColor, (i == (taskCount - 1)),
-                    config.lockToAppEnabled, !isStackTask, icon, iconFilename, t.bounds);
-            task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
-            if (DEBUG) {
-                Log.d(TAG, activityLabel + " bounds: " + t.bounds);
-            }
+            Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
+                    thumbnail, title, contentDescription, activityColor, !isStackTask,
+                    (i == (taskCount - 1)), config.lockToAppEnabled, t.bounds, t.taskDescription);
 
             allTasks.add(task);
         }
@@ -200,19 +175,13 @@
      */
     public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
             TaskResourceLoadQueue loadQueue) {
-        if (DEBUG) Log.d(TAG, "executePlan, # tasks: " + opts.numVisibleTasks +
-                ", # thumbnails: " + opts.numVisibleTaskThumbnails +
-                ", running task id: " + opts.runningTaskId);
-
         RecentsConfiguration config = Recents.getConfiguration();
-        SystemServicesProxy ssp = Recents.getSystemServices();
         Resources res = mContext.getResources();
 
         // Iterate through each of the tasks and load them according to the load conditions.
         ArrayList<Task> tasks = mStack.getStackTasks();
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
-            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
             Task task = tasks.get(i);
             Task.TaskKey taskKey = task.key;
 
@@ -226,17 +195,15 @@
             }
 
             if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
-                if (task.activityIcon == null) {
-                    if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
-                    task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
-                            ssp, res, true);
+                if (task.icon == null) {
+                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
+                            res, true);
                 }
             }
             if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
                 if (task.thumbnail == null || isRunningTask) {
-                    if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
                     if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
-                        task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, true);
+                        task.thumbnail = loader.getAndUpdateThumbnail(taskKey, true);
                     } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
                         loadQueue.addTask(task);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index c72d166..28338d83 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -93,23 +93,23 @@
     Handler mMainThreadHandler;
 
     TaskResourceLoadQueue mLoadQueue;
-    TaskKeyLruCache<Drawable> mApplicationIconCache;
+    TaskKeyLruCache<Drawable> mIconCache;
     TaskKeyLruCache<Bitmap> mThumbnailCache;
     Bitmap mDefaultThumbnail;
-    BitmapDrawable mDefaultApplicationIcon;
+    BitmapDrawable mDefaultIcon;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
     public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
-            TaskKeyLruCache<Drawable> applicationIconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
-            Bitmap defaultThumbnail, BitmapDrawable defaultApplicationIcon) {
+            TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+            Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
         mLoadQueue = loadQueue;
-        mApplicationIconCache = applicationIconCache;
+        mIconCache = iconCache;
         mThumbnailCache = thumbnailCache;
         mDefaultThumbnail = defaultThumbnail;
-        mDefaultApplicationIcon = defaultApplicationIcon;
+        mDefaultIcon = defaultIcon;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -163,30 +163,30 @@
                     // Load the next item from the queue
                     final Task t = mLoadQueue.nextTask();
                     if (t != null) {
-                        Drawable cachedIcon = mApplicationIconCache.get(t.key);
+                        Drawable cachedIcon = mIconCache.get(t.key);
                         Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
 
-                        // Load the application icon if it is stale or we haven't cached one yet
+                        // Load the icon if it is stale or we haven't cached one yet
                         if (cachedIcon == null) {
-                            cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp,
-                                    mContext.getResources());
+                            cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription,
+                                    t.key.userId, mContext.getResources());
 
                             if (cachedIcon == null) {
                                 ActivityInfo info = ssp.getActivityInfo(
                                         t.key.getComponent(), t.key.userId);
                                 if (info != null) {
                                     if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
-                                    cachedIcon = ssp.getActivityIcon(info, t.key.userId);
+                                    cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId);
                                 }
                             }
 
                             if (cachedIcon == null) {
-                                cachedIcon = mDefaultApplicationIcon;
+                                cachedIcon = mDefaultIcon;
                             }
 
                             // At this point, even if we can't load the icon, we will set the
                             // default icon.
-                            mApplicationIconCache.put(t.key, cachedIcon);
+                            mIconCache.put(t.key, cachedIcon);
                         }
                         // Load the thumbnail if it is stale or we haven't cached one yet
                         if (cachedThumbnail == null) {
@@ -234,25 +234,6 @@
             }
         }
     }
-
-    Drawable getTaskDescriptionIcon(Task.TaskKey taskKey, Bitmap iconBitmap, String iconFilename,
-            SystemServicesProxy ssp, Resources res) {
-        Bitmap tdIcon = null;
-        if (iconBitmap != null) {
-            tdIcon = iconBitmap;
-        } else {
-            try {
-                tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename,
-                        taskKey.userId);
-            } catch (Exception e) {
-                // TODO: Investigate for b/26221779
-            }
-        }
-        if (tdIcon != null) {
-            return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
-        }
-        return null;
-    }
 }
 
 /**
@@ -269,7 +250,7 @@
     // active time.  Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
     // package in the cache has been updated, so that we may remove it.
     private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
-    private final TaskKeyLruCache<Drawable> mApplicationIconCache;
+    private final TaskKeyLruCache<Drawable> mIconCache;
     private final TaskKeyLruCache<Bitmap> mThumbnailCache;
     private final TaskKeyLruCache<String> mActivityLabelCache;
     private final TaskKeyLruCache<String> mContentDescriptionCache;
@@ -282,7 +263,7 @@
     private int mNumVisibleThumbnailsLoaded;
 
     int mDefaultTaskBarBackgroundColor;
-    BitmapDrawable mDefaultApplicationIcon;
+    BitmapDrawable mDefaultIcon;
     Bitmap mDefaultThumbnail;
 
     public RecentsTaskLoader(Context context) {
@@ -302,22 +283,22 @@
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         mDefaultThumbnail.setHasAlpha(false);
         mDefaultThumbnail.eraseColor(0xFFffffff);
-        mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
+        mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
 
         // Initialize the proxy, cache and loaders
         int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
         mLoadQueue = new TaskResourceLoadQueue();
-        mApplicationIconCache = new TaskKeyLruCache<>(iconCacheSize);
+        mIconCache = new TaskKeyLruCache<>(iconCacheSize);
         mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize);
         mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks);
         mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks);
         mActivityInfoCache = new LruCache(numRecentTasks);
-        mLoader = new BackgroundTaskLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
-                mDefaultThumbnail, mDefaultApplicationIcon);
+        mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mThumbnailCache,
+                mDefaultThumbnail, mDefaultIcon);
     }
 
     /** Returns the size of the app icon cache. */
-    public int getApplicationIconCacheSize() {
+    public int getIconCacheSize() {
         return mMaxIconCacheSize;
     }
 
@@ -355,33 +336,33 @@
 
     /** Acquires the task resource data directly from the pool. */
     public void loadTaskData(Task t) {
-        Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
+        Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
         Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
 
         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
         // use the default assets in their place until they load
-        boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
-        applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
+        boolean requiresLoad = (icon == null) || (thumbnail == null);
+        icon = icon != null ? icon : mDefaultIcon;
         if (requiresLoad) {
             mLoadQueue.addTask(t);
         }
-        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
+        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
     }
 
     /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
         mLoadQueue.removeTask(t);
-        t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
+        t.notifyTaskDataUnloaded(null, mDefaultIcon);
     }
 
     /** Completely removes the resource data from the pool. */
     public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
         mLoadQueue.removeTask(t);
         mThumbnailCache.remove(t.key);
-        mApplicationIconCache.remove(t.key);
+        mIconCache.remove(t.key);
         mActivityInfoCache.remove(t.key.getComponent());
         if (notifyTaskDataUnloaded) {
-            t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
+            t.notifyTaskDataUnloaded(null, mDefaultIcon);
         }
     }
 
@@ -403,14 +384,14 @@
                 } else if (config.svelteLevel >= RecentsConfiguration.SVELTE_DISABLE_CACHE) {
                     mThumbnailCache.evictAll();
                 }
-                mApplicationIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
+                mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
                         mMaxIconCacheSize / 2));
                 break;
             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
             case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                 // We are leaving recents, so trim the data a bit
                 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
-                mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
+                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
                 mActivityInfoCache.trimToSize(Math.max(1,
                         ActivityManager.getMaxRecentTasksStatic() / 2));
                 break;
@@ -418,7 +399,7 @@
             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                 // We are going to be low on memory
                 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
-                mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
+                mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
                 mActivityInfoCache.trimToSize(Math.max(1,
                         ActivityManager.getMaxRecentTasksStatic() / 4));
                 break;
@@ -426,7 +407,7 @@
             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                 // We are low on memory, so release everything
                 mThumbnailCache.evictAll();
-                mApplicationIconCache.evictAll();
+                mIconCache.evictAll();
                 mActivityInfoCache.evictAll();
                 // The cache is small, only clear the label cache when we are critical
                 mActivityLabelCache.evictAll();
@@ -440,8 +421,9 @@
     /**
      * Returns the cached task label if the task key is not expired, updating the cache if it is.
      */
-    String getAndUpdateActivityLabel(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
-                                     SystemServicesProxy ssp) {
+    String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the task description label if it exists
         if (td != null && td.getLabel() != null) {
             return td.getLabel();
@@ -452,7 +434,7 @@
             return label;
         }
         // All short paths failed, load the label from the activity info and cache it
-        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
         if (activityInfo != null) {
             label = ssp.getActivityLabel(activityInfo);
             mActivityLabelCache.put(taskKey, label);
@@ -468,7 +450,9 @@
      * cache if it is.
      */
     String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
-            SystemServicesProxy ssp, Resources res) {
+            Resources res) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the cached content description if it exists
         String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
         if (label != null) {
@@ -493,28 +477,29 @@
      * Returns the cached task icon if the task key is not expired, updating the cache if it is.
      */
     Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
-            SystemServicesProxy ssp, Resources res, boolean loadIfNotCached) {
+            Resources res, boolean loadIfNotCached) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the cached activity icon if it exists
-        Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
+        Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
         if (icon != null) {
             return icon;
         }
 
         if (loadIfNotCached) {
             // Return and cache the task description icon if it exists
-            icon = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
-                    td.getIconFilename(), ssp, res);
+            icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res);
             if (icon != null) {
-                mApplicationIconCache.put(taskKey, icon);
+                mIconCache.put(taskKey, icon);
                 return icon;
             }
 
             // Load the icon from the activity info and cache it
-            ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+            ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
             if (activityInfo != null) {
-                icon = ssp.getActivityIcon(activityInfo, taskKey.userId);
+                icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId);
                 if (icon != null) {
-                    mApplicationIconCache.put(taskKey, icon);
+                    mIconCache.put(taskKey, icon);
                     return icon;
                 }
             }
@@ -526,8 +511,9 @@
     /**
      * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
      */
-    Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, SystemServicesProxy ssp,
-            boolean loadIfNotCached) {
+    Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+
         // Return the cached thumbnail if it exists
         Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
         if (thumbnail != null) {
@@ -550,7 +536,8 @@
     }
 
     /**
-     * Returns the task's primary color.
+     * Returns the task's primary color if possible, defaulting to the default color if there is
+     * no specified primary color.
      */
     int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
         if (td != null && td.getPrimaryColor() != 0) {
@@ -563,7 +550,8 @@
      * Returns the activity info for the given task key, retrieving one from the system if the
      * task key is expired.
      */
-    private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey, SystemServicesProxy ssp) {
+    private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
         ComponentName cn = taskKey.getComponent();
         ActivityInfo activityInfo = mActivityInfoCache.get(cn);
         if (activityInfo == null) {
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 73c0adb..34a0e52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.model;
 
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -92,24 +93,46 @@
     }
 
     public TaskKey key;
+
+    /**
+     * The group will be computed separately from the initialization of the task
+     */
     public TaskGrouping group;
-    // The taskAffiliationId is the task id of the parent task or itself if it is not affiliated with any task
-    public int taskAffiliationId;
-    public int taskAffiliationColor;
-    public boolean isLaunchTarget;
-    public Drawable applicationIcon;
-    public Drawable activityIcon;
+    /**
+     * The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
+     * with any task.
+     */
+    public int affiliationTaskId;
+    public int affiliationColor;
+
+    /**
+     * The icon is the task description icon (if provided), which falls back to the activity icon,
+     * which can then fall back to the application icon.
+     */
+    public Drawable icon;
+    public Bitmap thumbnail;
+    public String title;
     public String contentDescription;
-    public String activityLabel;
     public int colorPrimary;
     public boolean useLightOnPrimaryColor;
-    public Bitmap thumbnail;
+
+    /**
+     * The bounds of the task, used only if it is a freeform task.
+     */
+    public Rect bounds;
+
+    /**
+     * The task description for this task, only used to reload task icons.
+     */
+    public ActivityManager.TaskDescription taskDescription;
+
+    /**
+     * The state isLaunchTarget will be set for the correct task upon launching Recents.
+     */
+    public boolean isLaunchTarget;
+    public boolean isHistorical;
     public boolean lockToThisTask;
     public boolean lockToTaskEnabled;
-    public boolean isHistorical;
-    public Bitmap icon;
-    public String iconFilename;
-    public Rect bounds;
 
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
@@ -117,45 +140,46 @@
         // Do nothing
     }
 
-    public Task(TaskKey key, int taskAffiliation, int taskAffiliationColor,
-                String activityTitle, String contentDescription, Drawable activityIcon,
-                int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled,
-                boolean isHistorical, Bitmap icon, String iconFilename, Rect bounds) {
-        boolean isInAffiliationGroup = (taskAffiliation != key.id);
-        boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
+    public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
+                Bitmap thumbnail, String title, String contentDescription, int colorPrimary,
+                boolean isHistorical, boolean lockToThisTask, boolean lockToTaskEnabled,
+                Rect bounds, ActivityManager.TaskDescription taskDescription) {
+        boolean isInAffiliationGroup = (affiliationTaskId != key.id);
+        boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
         this.key = key;
-        this.taskAffiliationId = taskAffiliation;
-        this.taskAffiliationColor = taskAffiliationColor;
-        this.activityLabel = activityTitle;
+        this.affiliationTaskId = affiliationTaskId;
+        this.affiliationColor = affiliationColor;
+        this.icon = icon;
+        this.thumbnail = thumbnail;
+        this.title = title;
         this.contentDescription = contentDescription;
-        this.activityIcon = activityIcon;
-        this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
+        this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
                 Color.WHITE) > 3f;
+        this.bounds = bounds;
+        this.taskDescription = taskDescription;
+        this.isHistorical = isHistorical;
         this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
         this.lockToTaskEnabled = lockToTaskEnabled;
-        this.isHistorical = isHistorical;
-        this.icon = icon;
-        this.iconFilename = iconFilename;
-        this.bounds = bounds;
     }
 
     /** Copies the other task. */
     public void copyFrom(Task o) {
         this.key = o.key;
-        this.taskAffiliationId = o.taskAffiliationId;
-        this.taskAffiliationColor = o.taskAffiliationColor;
-        this.activityLabel = o.activityLabel;
+        this.group = o.group;
+        this.affiliationTaskId = o.affiliationTaskId;
+        this.affiliationColor = o.affiliationColor;
+        this.icon = o.icon;
+        this.thumbnail = o.thumbnail;
+        this.title = o.title;
         this.contentDescription = o.contentDescription;
-        this.activityIcon = o.activityIcon;
         this.colorPrimary = o.colorPrimary;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
+        this.bounds = o.bounds;
+        this.isLaunchTarget = o.isLaunchTarget;
+        this.isHistorical = o.isHistorical;
         this.lockToThisTask = o.lockToThisTask;
         this.lockToTaskEnabled = o.lockToTaskEnabled;
-        this.isHistorical = o.isHistorical;
-        this.icon = o.icon;
-        this.iconFilename = o.iconFilename;
-        this.bounds = o.bounds;
     }
 
     /**
@@ -200,7 +224,7 @@
 
     /** Notifies the callback listeners that this task has been loaded */
     public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
-        this.applicationIcon = applicationIcon;
+        this.icon = applicationIcon;
         this.thumbnail = thumbnail;
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
@@ -210,7 +234,7 @@
 
     /** Notifies the callback listeners that this task has been unloaded */
     public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultApplicationIcon) {
-        applicationIcon = defaultApplicationIcon;
+        icon = defaultApplicationIcon;
         thumbnail = defaultThumbnail;
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
@@ -222,7 +246,7 @@
      * Returns whether this task is affiliated with another task.
      */
     public boolean isAffiliatedTask() {
-        return key.id != taskAffiliationId;
+        return key.id != affiliationTaskId;
     }
 
     @Override
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 ad03b4e..d06012e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -354,7 +354,7 @@
                 if (t.isAffiliatedTask()) {
                     // If this task is affiliated with another parent in the stack, then the historical state of this
                     // task depends on the state of the parent task
-                    Task parentTask = taskIdMap.get(t.taskAffiliationId);
+                    Task parentTask = taskIdMap.get(t.affiliationTaskId);
                     if (parentTask != null) {
                         t = parentTask;
                     }
@@ -368,7 +368,7 @@
                 if (t.isAffiliatedTask()) {
                     // If this task is affiliated with another parent in the stack, then the historical state of this
                     // task depends on the state of the parent task
-                    Task parentTask = taskIdMap.get(t.taskAffiliationId);
+                    Task parentTask = taskIdMap.get(t.affiliationTaskId);
                     if (parentTask != null) {
                         t = parentTask;
                     }
@@ -716,7 +716,7 @@
             for (int i = 0; i < taskCount; i++) {
                 Task t = tasks.get(i);
                 TaskGrouping group;
-                int affiliation = t.taskAffiliationId > 0 ? t.taskAffiliationId :
+                int affiliation = t.affiliationTaskId > 0 ? t.affiliationTaskId :
                         IndividualTaskIdOffset + t.key.id;
                 if (mAffinitiesGroups.containsKey(affiliation)) {
                     group = getGroupWithAffiliation(affiliation);
@@ -737,7 +737,7 @@
                 // Ignore the groups that only have one task
                 if (taskCount <= 1) continue;
                 // Calculate the group color distribution
-                int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).taskAffiliationColor;
+                int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).affiliationColor;
                 float alphaStep = (1f - minAlpha) / taskCount;
                 float alpha = 1f;
                 for (int j = 0; j < taskCount; j++) {
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 135f0f9..0af7c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -157,7 +157,7 @@
             ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture,
             final ActivityOptions.OnAnimationStartedListener animStartedListener) {
         SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.startActivityFromRecents(mContext, task.key.id, task.activityLabel, opts)) {
+        if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
             // Keep track of the index of the task launch
             int taskIndexFromFront = 0;
             int taskIndex = stack.indexOfStackTask(task);
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 53c02cb..9b1315a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -132,10 +132,7 @@
         mHistoryButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                ReferenceCountedTrigger postHideStackAnimationTrigger = new ReferenceCountedTrigger(v.getContext());
-                postHideStackAnimationTrigger.increment();
-                EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
-                postHideStackAnimationTrigger.decrement();
+                EventBus.getDefault().send(new ShowHistoryEvent());
             }
         });
         addView(mHistoryButton);
@@ -576,8 +573,8 @@
     public final void onBusEvent(ShowHistoryEvent event) {
         // Hide the history button when the history view is shown
         hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
-                event.postHideStackAnimationTrigger);
-        event.postHideStackAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                event.getAnimationTrigger());
+        event.addPostAnimationCallback(new Runnable() {
             @Override
             public void run() {
                 setAlpha(0f);
@@ -589,7 +586,7 @@
         // Show the history button when the history view is hidden
         setAlpha(1f);
         showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
-                event.postHideHistoryAnimationTrigger);
+                event.getAnimationTrigger());
     }
 
     public final void onBusEvent(ShowHistoryButtonEvent event) {
@@ -609,10 +606,9 @@
      * Shows the history button.
      */
     private void showHistoryButton(final int duration) {
-        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
-        postAnimationTrigger.increment();
+        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         showHistoryButton(duration, postAnimationTrigger);
-        postAnimationTrigger.decrement();
+        postAnimationTrigger.flushLastDecrementRunnables();
     }
 
     private void showHistoryButton(final int duration,
@@ -638,10 +634,9 @@
      * Hides the history button.
      */
     private void hideHistoryButton(int duration) {
-        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
-        postAnimationTrigger.increment();
+        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         hideHistoryButton(duration, postAnimationTrigger);
-        postAnimationTrigger.decrement();
+        postAnimationTrigger.flushLastDecrementRunnables();
     }
 
     private void hideHistoryButton(int duration,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 37a0194..318801d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -188,12 +188,8 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL: {
                 if (mDragging) {
-                    ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
-                            mRv.getContext());
-                    postAnimationTrigger.increment();
                     EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
-                            mLastDropTarget, postAnimationTrigger));
-                    postAnimationTrigger.decrement();
+                            mLastDropTarget));
                     break;
                 }
             }
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 421e6a0..830d607 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -44,7 +44,6 @@
 import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -798,7 +797,7 @@
             TaskView frontMostTask = taskViews.get(taskViewCount - 1);
             event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
             event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
-            event.setContentDescription(frontMostTask.getTask().activityLabel);
+            event.setContentDescription(frontMostTask.getTask().title);
         }
         event.setItemCount(mStack.getStackTaskCount());
         event.setScrollY(mStackScroller.mScroller.getCurrY());
@@ -1022,8 +1021,7 @@
             if (launchTargetTask != null) {
                 occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
                         launchTargetTask);
-                hideTask = SystemServicesProxy.isFreeformStack(launchTargetTask.key.stackId) &&
-                        SystemServicesProxy.isFreeformStack(task.key.stackId);
+                hideTask = launchTargetTask.isFreeformTask() && task.isFreeformTask();
             }
             tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget,
                     offscreenY);
@@ -1495,7 +1493,6 @@
                 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
                         (isFreeformTask && event.dropTarget == mStackDropTarget);
 
-        event.postAnimationTrigger.increment();
         if (hasChangedStacks) {
             // Move the task to the right position in the stack (ie. the front of the stack if
             // freeform or the front of the stack if fullscreen).  Note, we MUST move the tasks
@@ -1508,7 +1505,7 @@
             updateLayout(true);
 
             // Move the task to the new stack in the system after the animation completes
-            event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            event.addPostAnimationCallback(new Runnable() {
                 @Override
                 public void run() {
                     SystemServicesProxy ssp = Recents.getSystemServices();
@@ -1516,8 +1513,8 @@
                 }
             });
         }
-        event.taskView.animate()
-                .withEndAction(event.postAnimationTrigger.decrementAsRunnable());
+        event.getAnimationTrigger().increment();
+        event.taskView.animate().withEndAction(event.getAnimationTrigger().decrementAsRunnable());
 
         // We translated the view but we need to animate it back from the current layout-space rect
         // to its final layout-space rect
@@ -1557,7 +1554,7 @@
         for (int i = 0; i < taskViewCount; i++) {
             TaskView tv = taskViews.get(i);
             Task task = tv.getTask();
-            if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
+            if (task.isFreeformTask()) {
                 tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
             }
         }
@@ -1578,9 +1575,9 @@
                     .setUpdateListener(null)
                     .setListener(null)
                     .withLayer()
-                    .withEndAction(event.postHideStackAnimationTrigger.decrementAsRunnable())
+                    .withEndAction(event.getAnimationTrigger().decrementAsRunnable())
                     .start();
-            event.postHideStackAnimationTrigger.increment();
+            event.getAnimationTrigger().increment();
         }
     }
 
@@ -1593,7 +1590,7 @@
         int taskViewCount = taskViews.size();
         for (int i = taskViewCount - 1; i >= 0; i--) {
             final TaskView tv = taskViews.get(i);
-            event.postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            event.addPostAnimationCallback(new Runnable() {
                 @Override
                 public void run() {
                     tv.animate()
@@ -1617,7 +1614,7 @@
 
         // Announce for accessibility
         tv.announceForAccessibility(getContext().getString(
-                R.string.accessibility_recents_item_dismissed, tv.getTask().activityLabel));
+                R.string.accessibility_recents_item_dismissed, tv.getTask().title));
 
         // Remove the task from the stack
         mStack.removeTask(task);
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 df7b9a6..a3e8b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -651,7 +651,7 @@
      */
     public void setFocusedState(boolean isFocused, boolean animated, boolean requestViewFocus) {
         if (DEBUG) {
-            Log.d(TAG, "setFocusedState: " + mTask.activityLabel + " focused: " + isFocused +
+            Log.d(TAG, "setFocusedState: " + mTask.title + " focused: " + isFocused +
                     " animated: " + animated + " requestViewFocus: " + requestViewFocus +
                     " isFocused(): " + isFocused() +
                     " isAccessibilityFocused(): " + isAccessibilityFocused());
@@ -771,7 +771,7 @@
 
     public final void onBusEvent(DragEndEvent event) {
         if (!(event.dropTarget instanceof TaskStack.DockState)) {
-            event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            event.addPostAnimationCallback(new Runnable() {
                 @Override
                 public void run() {
                     // Animate the drag view back from where it is, to the view location, then after
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0271ccd..9a2ffe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -60,8 +60,8 @@
     // Header views
     ImageView mMoveTaskButton;
     ImageView mDismissButton;
-    ImageView mApplicationIcon;
-    TextView mActivityDescription;
+    ImageView mIconView;
+    TextView mTitleView;
     int mMoveTaskTargetStackId = INVALID_STACK_ID;
 
     // Header drawables
@@ -128,16 +128,16 @@
     @Override
     protected void onFinishInflate() {
         // Initialize the icon and description views
-        mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
-        mApplicationIcon.setOnLongClickListener(this);
-        mActivityDescription = (TextView) findViewById(R.id.activity_description);
+        mIconView = (ImageView) findViewById(R.id.icon);
+        mIconView.setOnLongClickListener(this);
+        mTitleView = (TextView) findViewById(R.id.title);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
         mDismissButton.setOnClickListener(this);
         mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
 
         // Hide the backgrounds if they are ripple drawables
-        if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
-            mApplicationIcon.setBackground(null);
+        if (mIconView.getBackground() instanceof RippleDrawable) {
+            mIconView.setBackground(null);
         }
 
         mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(
@@ -158,8 +158,8 @@
     public void onTaskViewSizeChanged(int width, int height) {
         mTaskViewRect.set(0, 0, width, height);
         boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
-        int appIconWidth = mApplicationIcon.getMeasuredWidth();
-        int activityDescWidth = mActivityDescription.getMeasuredWidth();
+        int appIconWidth = mIconView.getMeasuredWidth();
+        int activityDescWidth = mTitleView.getMeasuredWidth();
         int dismissIconWidth = mDismissButton.getMeasuredWidth();
         int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE
                 ? mMoveTaskButton.getMeasuredWidth()
@@ -168,26 +168,26 @@
         // Priority-wise, we show the activity icon first, the dismiss icon if there is room, the
         // move-task icon if there is room, and then finally, the activity label if there is room
         if (width < (appIconWidth + dismissIconWidth)) {
-            mActivityDescription.setVisibility(View.INVISIBLE);
+            mTitleView.setVisibility(View.INVISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.INVISIBLE);
             }
             mDismissButton.setVisibility(View.INVISIBLE);
         } else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth)) {
-            mActivityDescription.setVisibility(View.INVISIBLE);
+            mTitleView.setVisibility(View.INVISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.INVISIBLE);
             }
             mDismissButton.setVisibility(View.VISIBLE);
         } else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth +
                 activityDescWidth)) {
-            mActivityDescription.setVisibility(View.INVISIBLE);
+            mTitleView.setVisibility(View.INVISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.VISIBLE);
             }
             mDismissButton.setVisibility(View.VISIBLE);
         } else {
-            mActivityDescription.setVisibility(View.VISIBLE);
+            mTitleView.setVisibility(View.VISIBLE);
             if (updateMoveTaskButton) {
                 mMoveTaskButton.setVisibility(View.VISIBLE);
             }
@@ -233,15 +233,13 @@
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
-        if (t.activityIcon != null) {
-            mApplicationIcon.setImageDrawable(t.activityIcon);
-        } else if (t.applicationIcon != null) {
-            mApplicationIcon.setImageDrawable(t.applicationIcon);
+        if (t.icon != null) {
+            mIconView.setImageDrawable(t.icon);
         }
-        if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
-            mActivityDescription.setText(t.activityLabel);
+        if (!mTitleView.getText().toString().equals(t.title)) {
+            mTitleView.setText(t.title);
         }
-        mActivityDescription.setContentDescription(t.contentDescription);
+        mTitleView.setContentDescription(t.contentDescription);
 
         // Try and apply the system ui tint
         int existingBgColor = (getBackground() instanceof ColorDrawable) ?
@@ -254,7 +252,7 @@
                 R.color.recents_task_bar_light_text_color);
         int taskBarViewDarkTextColor = getResources().getColor(
                 R.color.recents_task_bar_dark_text_color);
-        mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
+        mTitleView.setTextColor(t.useLightOnPrimaryColor ?
                 taskBarViewLightTextColor : taskBarViewDarkTextColor);
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
@@ -281,15 +279,15 @@
 
         // In accessibility, a single click on the focused app info button will show it
         if (ssp.isTouchExplorationEnabled()) {
-            mApplicationIcon.setOnClickListener(this);
+            mIconView.setOnClickListener(this);
         }
     }
 
     /** Unbinds the bar view from the task */
     void unbindFromTask() {
         mTask = null;
-        mApplicationIcon.setImageDrawable(null);
-        mApplicationIcon.setOnClickListener(null);
+        mIconView.setImageDrawable(null);
+        mIconView.setOnClickListener(null);
         mMoveTaskButton.setOnClickListener(null);
     }
 
@@ -357,7 +355,7 @@
 
     @Override
     public void onClick(View v) {
-        if (v == mApplicationIcon) {
+        if (v == mIconView) {
             // In accessibility, a single click on the focused app info button will show it
             EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
         } else if (v == mDismissButton) {
@@ -379,7 +377,7 @@
 
     @Override
     public boolean onLongClick(View v) {
-        if (v == mApplicationIcon) {
+        if (v == mIconView) {
             EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
             return true;
         }