Merge "Make entering recents a lot faster" into mnc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index bbd3e60..7d2b5c87 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -32,6 +32,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -40,6 +41,7 @@
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
+
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
@@ -184,12 +186,16 @@
 
     // Header (for transition)
     TaskViewHeader mHeaderBar;
+    final Object mHeaderBarLock = new Object();
     TaskStackView mDummyStackView;
 
     // Variables to keep track of if we need to start recents after binding
     boolean mTriggeredFromAltTab;
     long mLastToggleTime;
 
+    Bitmap mThumbnailTransitionBitmapCache;
+    Task mThumbnailTransitionBitmapCacheKey;
+
     public Recents() {
     }
 
@@ -360,13 +366,16 @@
     void preloadRecentsInternal() {
         // Preload only the raw task list into a new load plan (which will be consumed by the
         // RecentsActivity)
+        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+        MutableBoolean topTaskHome = new MutableBoolean(true);
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         sInstanceLoadPlan = loader.createLoadPlan(mContext);
-
-        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
-        MutableBoolean isTopTaskHome = new MutableBoolean(true);
-        if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
-            sInstanceLoadPlan.preloadRawTasks(isTopTaskHome.value);
+        if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
+            sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
+            loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
+            TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);
+            preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,
+                    topTaskHome.value);
         }
     }
 
@@ -513,12 +522,14 @@
         algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
         Rect taskViewSize = algo.getUntransformedTaskViewSize();
         int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
-                false);
-        mHeaderBar.measure(
-                View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
-                View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
-        mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
+        synchronized (mHeaderBarLock) {
+            mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
+                    false);
+            mHeaderBar.measure(
+                    View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
+                    View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
+            mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
+        }
     }
 
     /** Prepares the search bar app widget */
@@ -607,30 +618,27 @@
      */
     ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
             TaskStack stack, TaskStackView stackView) {
+
         // Update the destination rect
         Task toTask = new Task();
         TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
                 topTask.id, toTask);
-        if (toTransform != null && toTask.key != null) {
-            Rect toTaskRect = toTransform.rect;
-            int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
-            int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
-            Bitmap thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
-                    Bitmap.Config.ARGB_8888);
-            if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
-                thumbnail.eraseColor(0xFFff0000);
-            } else {
-                Canvas c = new Canvas(thumbnail);
-                c.scale(toTransform.scale, toTransform.scale);
-                mHeaderBar.rebindToTask(toTask);
-                mHeaderBar.draw(c);
-                c.setBitmap(null);
-            }
-            Bitmap thumbnailImmutable = thumbnail.createAshmemBitmap();
-
+        Rect toTaskRect = toTransform.rect;
+        Bitmap thumbnail;
+        if (mThumbnailTransitionBitmapCacheKey != null
+                && mThumbnailTransitionBitmapCacheKey.key != null
+                && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
+            thumbnail = mThumbnailTransitionBitmapCache;
+            mThumbnailTransitionBitmapCacheKey = null;
+            mThumbnailTransitionBitmapCache = null;
+        } else {
+            preloadIcon(topTask);
+            thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+        }
+        if (thumbnail != null) {
             mStartAnimationTriggered = false;
             return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
-                    thumbnailImmutable, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+                    thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
                     toTaskRect.height(), mHandler, this);
         }
 
@@ -638,6 +646,72 @@
         return getUnknownTransitionActivityOptions();
     }
 
+    /**
+     * Preloads the icon of a task.
+     */
+    void preloadIcon(ActivityManager.RunningTaskInfo task) {
+
+        // Ensure that we load the running task's icon
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.runningTaskId = task.id;
+        launchOpts.loadThumbnails = false;
+        launchOpts.onlyLoadForCache = true;
+        RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
+    }
+
+    /**
+     * Caches the header thumbnail used for a window animation asynchronously into
+     * {@link #mThumbnailTransitionBitmapCache}.
+     */
+    void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
+            TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
+        preloadIcon(topTask);
+
+        // Update the destination rect
+        mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+        final Task toTask = new Task();
+        final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+                topTask.id, toTask);
+        new AsyncTask<Void, Void, Bitmap>() {
+            @Override
+            protected Bitmap doInBackground(Void... params) {
+                return drawThumbnailTransitionBitmap(toTask, toTransform);
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap bitmap) {
+                mThumbnailTransitionBitmapCache = bitmap;
+                mThumbnailTransitionBitmapCacheKey = toTask;
+            }
+        }.execute();
+    }
+
+    /**
+     * Draws the header of a task used for the window animation into a bitmap.
+     */
+    Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+        if (toTransform != null && toTask.key != null) {
+            Bitmap thumbnail;
+            synchronized (mHeaderBarLock) {
+                int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
+                int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+                thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
+                        Bitmap.Config.ARGB_8888);
+                if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+                    thumbnail.eraseColor(0xFFff0000);
+                } else {
+                    Canvas c = new Canvas(thumbnail);
+                    c.scale(toTransform.scale, toTransform.scale);
+                    mHeaderBar.rebindToTask(toTask);
+                    mHeaderBar.draw(c);
+                    c.setBitmap(null);
+                }
+            }
+            return thumbnail.createAshmemBitmap();
+        }
+        return null;
+    }
+
     /** Returns the transition rect for the given task id. */
     TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
             int runningTaskId, Task runningTaskOut) {
@@ -694,7 +768,9 @@
             return;
         }
 
-        loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+        if (!sInstanceLoadPlan.hasTasks()) {
+            loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+        }
         ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
         TaskStack stack = stacks.get(0);
 
@@ -706,12 +782,6 @@
         boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
 
         if (useThumbnailTransition) {
-            // Ensure that we load the running task's icon
-            RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-            launchOpts.runningTaskId = topTask.id;
-            launchOpts.loadThumbnails = false;
-            launchOpts.onlyLoadForCache = true;
-            loader.loadTasks(mContext, sInstanceLoadPlan, launchOpts);
 
             // Try starting with a thumbnail transition
             ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index ad97f91..9404dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -441,6 +441,10 @@
         if (mConfig.launchedHasConfigurationChanged) {
             onEnterAnimationTriggered();
         }
+
+        if (!mConfig.launchedHasConfigurationChanged) {
+            mRecentsView.disableLayersForOneFrame();
+        }
     }
 
     @Override
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 cec613c..1a21d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -431,6 +431,13 @@
         return false;
     }
 
+    public void disableLayersForOneFrame() {
+        List<TaskStackView> stackViews = getTaskStackViews();
+        for (int i = 0; i < stackViews.size(); i++) {
+            stackViews.get(i).disableLayersForOneFrame();
+        }
+    }
+
     /**** TaskStackView.TaskStackCallbacks Implementation ****/
 
     @Override
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 5f151e8..5711cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -19,6 +19,7 @@
 import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.content.Context;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.view.LayoutInflater;
@@ -63,7 +64,6 @@
 
         public void onTaskResize(Task t);
     }
-
     RecentsConfiguration mConfig;
 
     TaskStack mStack;
@@ -81,7 +81,6 @@
     boolean mDismissAllButtonAnimating;
     int mFocusedTaskIndex = -1;
     int mPrevAccessibilityFocusedIndex = -1;
-
     // Optimizations
     int mStackViewsAnimationDuration;
     boolean mStackViewsDirty = true;
@@ -99,6 +98,7 @@
     ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>();
     List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>();
     LayoutInflater mInflater;
+    boolean mLayersDisabled;
 
     // A convenience update listener to request updating clipping of tasks
     ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
@@ -375,7 +375,9 @@
 
                 if (tv == null) {
                     tv = mViewPool.pickUpViewFromPool(task, task);
-
+                    if (mLayersDisabled) {
+                        tv.disableLayersForOneFrame();
+                    }
                     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
@@ -1031,6 +1033,20 @@
         mUIDozeTrigger.poke();
     }
 
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        mLayersDisabled = false;
+        super.dispatchDraw(canvas);
+    }
+
+    public void disableLayersForOneFrame() {
+        mLayersDisabled = true;
+        List<TaskView> taskViews = getTaskViews();
+        for (int i = 0; i < taskViews.size(); i++) {
+            taskViews.get(i).disableLayersForOneFrame();
+        }
+    }
+
     /**** TaskStackCallbacks Implementation ****/
 
     @Override
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 4a5fef3..b0247cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -651,6 +651,10 @@
         }
     }
 
+    public void disableLayersForOneFrame() {
+        mHeaderView.disableLayersForOneFrame();
+    }
+
     /**** TaskCallbacks Implementation ****/
 
     /** Binds this task view to the task */
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 82f0f31..1a22cae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -80,6 +80,8 @@
     Paint mDimLayerPaint = new Paint();
     PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
 
+    boolean mLayersDisabled;
+
     public TaskViewHeader(Context context) {
         this(context, null);
     }
@@ -172,7 +174,9 @@
     void setDimAlpha(int alpha) {
         mDimColorFilter.setColor(Color.argb(alpha, 0, 0, 0));
         mDimLayerPaint.setColorFilter(mDimColorFilter);
-        setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+        if (!mLayersDisabled) {
+            setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+        }
     }
 
     /** Returns the secondary color for a primary color. */
@@ -304,6 +308,28 @@
         return new int[] {};
     }
 
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+        if (mLayersDisabled) {
+            mLayersDisabled = false;
+            postOnAnimation(new Runnable() {
+                @Override
+                public void run() {
+                    mLayersDisabled = false;
+                    setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+                }
+            });
+        }
+    }
+
+    public void disableLayersForOneFrame() {
+        mLayersDisabled = true;
+
+        // Disable layer for a frame so we can draw our first frame faster.
+        setLayerType(LAYER_TYPE_NONE, null);
+    }
+
     /** Notifies the associated TaskView has been focused. */
     void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
         // If we are not animating the visible state, just return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index fa172a4..f05ac1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1030,9 +1030,7 @@
 
     @Override
     public void toggleRecentApps() {
-        int msg = MSG_TOGGLE_RECENTS_APPS;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
+        toggleRecents();
     }
 
     @Override