Refactoring to support groups.

- Removing RecentService, determining animations just in time
- Fixing a few issues with animations of newly picked up tasks from the pool
- Moving helper classes into sub package

Change-Id: Ie10385d1f9ca79eea918b16932f56b60e2802304
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
index 6568b1a..49a7ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -31,7 +30,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 
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 f203d3e..87d45ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,7 +34,7 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -438,23 +438,25 @@
         }
 
         // Upfront the processing of the thumbnail
-        TaskViewTransform transform;
+        TaskViewTransform transform = new TaskViewTransform();
         View sourceView = tv;
         int offsetX = 0;
         int offsetY = 0;
         int stackScroll = stackView.getStackScroll();
+        TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+        stack.getGroupIndexForTask(task, groupTaskIndex);
         if (tv == null) {
             // If there is no actual task view, then use the stack view as the source view
             // and then offset to the expected transform rect, but bound this to just
             // outside the display rect (to ensure we don't animate from too far away)
             sourceView = stackView;
-            transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
-                    stackScroll);
+            transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+                    groupTaskIndex.taskIndex, stackScroll, transform);
             offsetX = transform.rect.left;
             offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
         } else {
-            transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
-                    stackScroll);
+            transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+                    groupTaskIndex.taskIndex, stackScroll, transform);
         }
 
         // Compute the thumbnail to scale up from
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index cae6bd7..8409227 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -28,7 +28,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 9c60603..4ed3b59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -34,7 +34,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 
 
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 dd47fddf..4a1faee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -31,18 +31,19 @@
 import android.widget.FrameLayout;
 import android.widget.OverScroller;
 import com.android.systemui.R;
-import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.DozeTrigger;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.ReferenceCountedTrigger;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Set;
 
 
@@ -180,7 +181,18 @@
         mStackViewsDirty = true;
     }
 
-    /** Finds the child view given a specific task */
+    /** Returns a mapping of child view to Task. */
+    HashMap<Task, TaskView> getTaskChildViewMap() {
+        HashMap<Task, TaskView> taskViewMap = new HashMap<Task, TaskView>();
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            TaskView tv = (TaskView) getChildAt(i);
+            taskViewMap.put(tv.getTask(), tv);
+        }
+        return taskViewMap;
+    }
+
+    /** Finds the child view given a specific task. */
     TaskView getChildViewForTask(Task t) {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -225,9 +237,11 @@
         }
 
         // Update the stack transforms
+        TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
         for (int i = taskCount - 1; i >= 0; i--) {
-            TaskViewTransform transform = mStackAlgorithm.getStackTransform(i, stackScroll,
-                    taskTransforms.get(i));
+            mStack.getGroupIndexForTask(tasks.get(i), groupTaskIndex);
+            TaskViewTransform transform = mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex,
+                    groupTaskIndex.taskIndex, stackScroll, taskTransforms.get(i));
             if (transform.visible) {
                 if (frontMostVisibleIndex < 0) {
                     frontMostVisibleIndex = i;
@@ -253,6 +267,11 @@
         if (visibleRangeOut != null) {
             visibleRangeOut[0] = frontMostVisibleIndex;
             visibleRangeOut[1] = backMostVisibleIndex;
+            if (Console.Enabled) {
+                Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
+                        "[TaskStackView|updateStackTransforms]",
+                        "Back: " + backMostVisibleIndex + " Front: " + frontMostVisibleIndex);
+            }
         }
     }
 
@@ -278,58 +297,57 @@
                     "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
         }
         if (mStackViewsDirty) {
-            // XXX: Consider using TaskViewTransform pool to prevent allocations
-            // XXX: Iterate children views, update transforms and remove all that are not visible
-            //      For all remaining tasks, update transforms and if visible add the view
-
             // Get all the task transforms
-            int[] visibleRange = mTmpVisibleRange;
-            int stackScroll = getStackScroll();
             ArrayList<Task> tasks = mStack.getTasks();
+            int stackScroll = getStackScroll();
+            int[] visibleRange = mTmpVisibleRange;
             updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
+            TaskViewTransform tmpTransform = new TaskViewTransform();
+            TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex();
 
-            // Update the visible state of all the tasks
-            int taskCount = tasks.size();
-            for (int i = 0; i < taskCount; i++) {
-                Task task = tasks.get(i);
-                TaskViewTransform transform = mCurrentTaskTransforms.get(i);
-                TaskView tv = getChildViewForTask(task);
-
-                if (transform.visible) {
-                    if (tv == null) {
-                        tv = mViewPool.pickUpViewFromPool(task, task);
-                        // When we are picking up a new view from the view pool, prepare it for any
-                        // following animation by putting it in a reasonable place
-                        if (mStackViewsAnimationDuration > 0 && i != 0) {
-                            int fromIndex = (transform.t < 0) ?
-                                    Math.max(0, (visibleRange[1] - 1)) :
-                                    Math.min(taskCount - 1, (visibleRange[0] + 1));
-                            tv.updateViewPropertiesToTaskTransform(
-                                    mStackAlgorithm.getStackTransform(fromIndex, stackScroll), 0);
-                        }
-                    }
-                } else {
-                    if (tv != null) {
-                        mViewPool.returnViewToPool(tv);
-                    }
-                }
-            }
-
-            // Update all the remaining view children
-            // NOTE: We have to iterate in reverse where because we are removing views directly
+            // Return all the invisible children to the pool
+            HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
             int childCount = getChildCount();
             for (int i = childCount - 1; i >= 0; i--) {
                 TaskView tv = (TaskView) getChildAt(i);
                 Task task = tv.getTask();
                 int taskIndex = mStack.indexOfTask(task);
-                if (taskIndex < 0 || !mCurrentTaskTransforms.get(taskIndex).visible) {
+                if (taskIndex < visibleRange[1] || taskIndex > visibleRange[0]) {
+                    taskChildViewMap.remove(task);
                     mViewPool.returnViewToPool(tv);
-                } else {
-                    tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
-                            mStackViewsAnimationDuration);
                 }
             }
 
+            // Pick up all the newly visible children and update all the existing children
+            boolean isValidVisibleRange = visibleRange[0] != -1 && visibleRange[1] != -1;
+            for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
+                Task task = tasks.get(i);
+                TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+                TaskView tv = taskChildViewMap.get(task);
+                int taskIndex = mStack.indexOfTask(task);
+
+                if (tv == null) {
+                    tv = mViewPool.pickUpViewFromPool(task, task);
+                    if (mStackViewsAnimationDuration > 0) {
+                        // For items in the list, put them in start animating them from the
+                        // approriate ends of the list where they are expected to appear
+                        Task fromTask = (transform.t < 0) ?
+                                tasks.get(visibleRange[1]) :
+                                tasks.get(visibleRange[0]);
+                        mStack.getGroupIndexForTask(fromTask, gti);
+                        tmpTransform = mStackAlgorithm.getStackTransform(
+                                (transform.t < 0) ? gti.groupIndex - 1 : gti.groupIndex + 1,
+                                (transform.t < 0) ? gti.taskIndex - 1 : gti.taskIndex + 1,
+                                stackScroll, tmpTransform);
+                        tv.updateViewPropertiesToTaskTransform(tmpTransform, 0);
+                    }
+                }
+
+                // Update and animate the task into place
+                tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
+                        mStackViewsAnimationDuration);
+            }
+
             if (Console.Enabled) {
                 Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
                         "  [TaskStackView|viewChildren]", "" + getChildCount());
@@ -357,7 +375,7 @@
     }
     /** Computes the initial stack scroll for the stack. */
     int getInitialStackScroll() {
-        if (mStack.getTaskCount() > 2) {
+        if (mStack.getGroupingCount() > 2) {
             return mMaxScroll - mStackAlgorithm.mTaskRect.height() / 2;
         }
         return mMaxScroll;
@@ -477,7 +495,7 @@
     /** Updates the min and max virtual scroll bounds */
     void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
         // Compute the min and max scroll values
-        mStackAlgorithm.computeMinMaxScroll(mStack.getTaskCount());
+        mStackAlgorithm.computeMinMaxScroll(mStack.getGroupingCount());
         mMinScroll = mStackAlgorithm.mMinScroll;
         mMaxScroll = mStackAlgorithm.mMaxScroll;
 
@@ -794,30 +812,37 @@
             return;
         }
 
-        // Animate all the task views into view
-        TaskViewTransform transform = mStackAlgorithm.getStackTransform(mStack.getTaskCount() - 1,
-                getInitialStackScroll());
-        ctx.taskRect = transform.rect;
-        ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
-        int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            TaskView tv = (TaskView) getChildAt(i);
-            ctx.stackViewIndex = i;
-            ctx.stackViewCount = childCount;
-            ctx.isFrontMost = (i == (getChildCount() - 1));
-            ctx.transform = mStackAlgorithm.getStackTransform(
-                    mStack.indexOfTask(tv.getTask()), getStackScroll());
-            tv.startEnterRecentsAnimation(ctx);
-        }
-
-        // Add a runnable to the post animation ref counter to clear all the views
-        ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-            @Override
-            public void run() {
-                // Start dozing
-                mUIDozeTrigger.startDozing();
+        if (mStack.getTaskCount() > 0) {
+            // Animate all the task views into view
+            TaskViewTransform transform = new TaskViewTransform();
+            TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+            mStack.getGroupIndexForTask(mStack.getFrontMostTask(), groupTaskIndex);
+            mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+                    getInitialStackScroll(), transform);
+            ctx.taskRect = transform.rect;
+            ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
+            int childCount = getChildCount();
+            for (int i = childCount - 1; i >= 0; i--) {
+                TaskView tv = (TaskView) getChildAt(i);
+                ctx.stackViewIndex = i;
+                ctx.stackViewCount = childCount;
+                ctx.isFrontMost = (i == (getChildCount() - 1));
+                ctx.transform = new TaskViewTransform();
+                mStack.getGroupIndexForTask(tv.getTask(), groupTaskIndex);
+                mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+                        getStackScroll(), ctx.transform);
+                tv.startEnterRecentsAnimation(ctx);
             }
-        });
+
+            // Add a runnable to the post animation ref counter to clear all the views
+            ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    // Start dozing
+                    mUIDozeTrigger.startDozing();
+                }
+            });
+        }
     }
 
     /** Requests this task stacks to start it's exit-recents animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index daa18bc..ab47757 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -19,7 +19,7 @@
 import android.graphics.Rect;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
 
 /* The layout logic for a TaskStackView */
 public class TaskStackViewLayoutAlgorithm {
@@ -94,17 +94,11 @@
         }
     }
 
-    /** Update/get the transform (creates a new TaskViewTransform) */
-    public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
-        TaskViewTransform transform = new TaskViewTransform();
-        return getStackTransform(indexInStack, stackScroll, transform);
-    }
-
     /** Update/get the transform */
-    public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
-                                               TaskViewTransform transformOut) {
+    public TaskViewTransform getStackTransform(int groupIndexInStack, int taskIndexInGroup,
+                                               int stackScroll, TaskViewTransform transformOut) {
         // Return early if we have an invalid index
-        if (indexInStack < 0) {
+        if (groupIndexInStack < 0) {
             transformOut.reset();
             return transformOut;
         }
@@ -113,7 +107,7 @@
         int numPeekCards = StackPeekNumCards;
         float overlapHeight = StackOverlapPct * mTaskRect.height();
         float peekHeight = StackPeekHeightPct * mStackRect.height();
-        float t = ((indexInStack * overlapHeight) - stackScroll) / overlapHeight;
+        float t = ((groupIndexInStack * overlapHeight) - stackScroll) / overlapHeight;
         float boundedT = Math.max(t, -(numPeekCards + 1));
 
         // Set the scale relative to its position
@@ -133,6 +127,7 @@
         } else {
             transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
+        transformOut.translationY += 100 * taskIndexInGroup;
 
         // Set the z translation
         int minZ = mConfig.taskViewTranslationZMinPx;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 304d45c..e186e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -22,7 +22,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 
 /* Handles touch events for a TaskStackView. */
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 6b06945..0d0fccc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -25,18 +25,14 @@
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
@@ -61,6 +57,7 @@
     Task mTask;
     boolean mTaskDataLoaded;
     boolean mIsFocused;
+    boolean mIsStub;
     boolean mClipViewInStack;
     Rect mTmpRect = new Rect();
     Paint mLayerPaint = new Paint();
@@ -133,8 +130,8 @@
 
         // Update the outline
         Outline o = new Outline();
-        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
-                mConfig.taskViewShadowOutlineBottomInsetPx, mConfig.taskViewRoundedCornerRadiusPx);
+        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+                mConfig.taskViewRoundedCornerRadiusPx);
         setOutline(o);
     }
 
@@ -469,6 +466,21 @@
         mBarView.disableHwLayers();
     }
 
+    /** Sets the stubbed state of this task view. */
+    void setStubState(boolean isStub) {
+        if (!mIsStub && isStub) {
+            // This is now a stub task view, so clip to the bar height, hide the thumbnail
+            setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
+            mThumbnailView.setVisibility(View.INVISIBLE);
+            // Temporary
+            mBarView.mActivityDescription.setText("Stub");
+        } else if (mIsStub && !isStub) {
+            setClipBounds(null);
+            mThumbnailView.setVisibility(View.VISIBLE);
+        }
+        mIsStub = isStub;
+    }
+
     /**
      * Returns whether this view should be clipped, or any views below should clip against this
      * view.
@@ -573,7 +585,9 @@
             mThumbnailView.rebindToTask(mTask);
             mBarView.rebindToTask(mTask);
             // Rebind any listeners
-            mBarView.mApplicationIcon.setOnClickListener(this);
+            if (Constants.DebugFlags.App.EnableTaskFiltering) {
+                mBarView.mApplicationIcon.setOnClickListener(this);
+            }
             mBarView.mDismissButton.setOnClickListener(this);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 if (mConfig.developerOptionsEnabled) {
@@ -592,7 +606,9 @@
             mThumbnailView.unbindFromTask();
             mBarView.unbindFromTask();
             // Unbind any listeners
-            mBarView.mApplicationIcon.setOnClickListener(null);
+            if (Constants.DebugFlags.App.EnableTaskFiltering) {
+                mBarView.mApplicationIcon.setOnClickListener(null);
+            }
             mBarView.mDismissButton.setOnClickListener(null);
             if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
                 mBarView.mApplicationIcon.setOnLongClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index fa97d2c..ec24198 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -17,7 +17,7 @@
 package com.android.systemui.recents.views;
 
 import android.graphics.Rect;
-import com.android.systemui.recents.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
 /* Common code related to view animations */
 public class ViewAnimation {