Merge "Fixing issue with animation not running after SystemUI crashes (Bug 14453240)"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 28de6ac..d763bd6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -227,6 +227,12 @@
     <!-- The radius of the rounded corners on a task view. -->
     <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
 
+    <!-- The min translation in the Z index for the last task. -->
+    <dimen name="recents_task_view_z_min">3dp</dimen>
+
+    <!-- The translation in the Z index for each task above the last task. -->
+    <dimen name="recents_task_view_z_increment">5dp</dimen>
+
     <!-- The amount of space a user has to scroll to dismiss any info panes. -->
     <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index f2e322d..396cb14 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -64,6 +64,17 @@
                 mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
                 mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
                 mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
+                Console.log(Constants.DebugFlags.App.RecentsComponent,
+                        "[RecentsComponent|RecentsMessageHandler|handleMessage]",
+                        "singleTaskRect: " + mSingleCountFirstTaskRect +
+                        " multipleTaskRect: " + mMultipleCountFirstTaskRect);
+
+                // If we had the update the animation rects as a result of onServiceConnected, then
+                // we check for whether we need to toggle the recents here.
+                if (mToggleRecentsUponServiceBound) {
+                    startAlternateRecentsActivity();
+                    mToggleRecentsUponServiceBound = false;
+                }
             }
         }
     }
@@ -78,11 +89,16 @@
             mService = new Messenger(service);
             mServiceIsBound = true;
 
-            // Toggle recents if this service connection was triggered by hitting the recents button
-            if (mToggleRecentsUponServiceBound) {
-                startAlternateRecentsActivity();
+            if (hasValidTaskRects()) {
+                // Toggle recents if this new service connection was triggered by hitting recents
+                if (mToggleRecentsUponServiceBound) {
+                    startAlternateRecentsActivity();
+                    mToggleRecentsUponServiceBound = false;
+                }
+            } else {
+                // Otherwise, update the animation rects before starting the recents if requested
+                updateAnimationRects();
             }
-            mToggleRecentsUponServiceBound = false;
         }
 
         @Override
@@ -191,6 +207,26 @@
     }
 
     public void onConfigurationChanged(Configuration newConfig) {
+        updateAnimationRects();
+    }
+
+    /** Binds to the recents implementation */
+    private void bindToRecentsService(boolean toggleRecentsUponConnection) {
+        mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
+        Intent intent = new Intent();
+        intent.setClassName(sRecentsPackage, sRecentsService);
+        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    /** Returns whether we have valid task rects to animate to. */
+    boolean hasValidTaskRects() {
+        return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 &&
+                mSingleCountFirstTaskRect.height() > 0 && mMultipleCountFirstTaskRect != null &&
+                mMultipleCountFirstTaskRect.width() > 0 && mMultipleCountFirstTaskRect.height() > 0;
+    }
+
+    /** Updates each of the task animation rects. */
+    void updateAnimationRects() {
         if (mServiceIsBound) {
             Resources res = mContext.getResources();
             int statusBarHeight = res.getDimensionPixelSize(
@@ -216,14 +252,6 @@
         }
     }
 
-    /** Binds to the recents implementation */
-    private void bindToRecentsService(boolean toggleRecentsUponConnection) {
-        mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
-        Intent intent = new Intent();
-        intent.setClassName(sRecentsPackage, sRecentsService);
-        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-    }
-
     /** Loads the first task thumbnail */
     Bitmap loadFirstTaskThumbnail() {
         SystemServicesProxy ssp = mSystemServicesProxy;
@@ -300,6 +328,49 @@
         return SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
     }
 
+    /** Creates the activity options for a thumbnail transition. */
+    ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+        // Loading from thumbnail
+        Bitmap thumbnail;
+        Bitmap firstThumbnail = loadFirstTaskThumbnail();
+        if (firstThumbnail != null) {
+            // Create the thumbnail
+            thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
+                    Bitmap.Config.ARGB_8888);
+            int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
+            Canvas c = new Canvas(thumbnail);
+            c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
+                    new Rect(0, 0, taskRect.width(), taskRect.height()), null);
+            c.setBitmap(null);
+            // Recycle the old thumbnail
+            firstThumbnail.recycle();
+        } else {
+            // Load the thumbnail from the screenshot if can't get one from the system
+            WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+            Display display = wm.getDefaultDisplay();
+            Bitmap screenshot = takeScreenshot(display);
+            if (screenshot != null) {
+                Resources res = mContext.getResources();
+                int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
+                int statusBarHeight = res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height);
+                thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(thumbnail);
+                c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight +
+                        size), new Rect(0, 0, taskRect.width(), taskRect.height()), null);
+                c.setBitmap(null);
+                // Recycle the temporary screenshot
+                screenshot.recycle();
+            } else {
+                return null;
+            }
+        }
+
+        return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail,
+                taskRect.left, taskRect.top, null);
+    }
+
     /** Starts the recents activity */
     void startAlternateRecentsActivity() {
         // If the user has toggled it too quickly, then just eat up the event here (it's better than
@@ -351,47 +422,28 @@
         // number of items in the list.
         List<ActivityManager.RecentTaskInfo> recentTasks =
                 ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
-        boolean hasMultipleTasks = hasMultipleRecentsTask(recentTasks);
+        Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect :
+                mSingleCountFirstTaskRect;
         boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
-        Rect taskRect = hasMultipleTasks ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect;
-        if (!isTopTaskHome && !isTaskExcludedFromRecents &&
-                (taskRect != null) && (taskRect.width() > 0) && (taskRect.height() > 0)) {
-            // Loading from thumbnail
-            Bitmap thumbnail;
-            Bitmap firstThumbnail = loadFirstTaskThumbnail();
-            if (firstThumbnail != null) {// Create the thumbnail
-                thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
-                        Bitmap.Config.ARGB_8888);
-                int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
-                Canvas c = new Canvas(thumbnail);
-                c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
-                        new Rect(0, 0, taskRect.width(), taskRect.height()), null);
-                c.setBitmap(null);
-                // Recycle the old thumbnail
-                firstThumbnail.recycle();
-            } else {
-                // Load the thumbnail from the screenshot if can't get one from the system
-                WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-                Display display = wm.getDefaultDisplay();
-                Bitmap screenshot = takeScreenshot(display);
-                Resources res = mContext.getResources();
-                int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
-                int statusBarHeight = res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height);
-                thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
-                        Bitmap.Config.ARGB_8888);
-                Canvas c = new Canvas(thumbnail);
-                c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight + size),
-                        new Rect(0, 0, taskRect.width(), taskRect.height()), null);
-                c.setBitmap(null);
-                // Recycle the temporary screenshot
-                screenshot.recycle();
-            }
+        boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents &&
+                hasValidTaskRects();
 
-            ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
-                    thumbnail, taskRect.left, taskRect.top, null);
-            startAlternateRecentsActivity(opts, true);
-        } else {
+        if (useThumbnailTransition) {
+            // Try starting with a thumbnail transition
+            ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
+            if (opts != null) {
+                startAlternateRecentsActivity(opts, true);
+            } else {
+                // Fall through below to the non-thumbnail transition
+                useThumbnailTransition = false;
+            }
+        }
+
+        // If there is no thumbnail transition, then just use a generic transition
+        // XXX: This should be different between home and from a recents-excluded app, perhaps the
+        //      recents-excluded app should still show up in recents, when the app is in the
+        //      foreground
+        if (!useThumbnailTransition) {
             ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
                     R.anim.recents_from_launcher_enter,
                     R.anim.recents_from_launcher_exit);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index bc8ab45..90ea873 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -91,11 +91,8 @@
             public static final int TaskStackOverscrollRange = 150;
             public static final int FilterStartDelay = 25;
 
-            // The amount to inverse scale the movement if we are overscrolling
-            public static final float TouchOverscrollScaleFactor = 3f;
-
             // The padding will be applied to the smallest dimension, and then applied to all sides
-            public static final float StackPaddingPct = 0.15f;
+            public static final float StackPaddingPct = 0.1f;
             // The overlap height relative to the task height
             public static final float StackOverlapPct = 0.65f;
             // The height of the peek space relative to the stack height
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 23a0179..d1a3954 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -49,6 +49,8 @@
     public int taskStackScrollDismissInfoPaneDistance;
     public int taskStackMaxDim;
     public int taskViewInfoPaneAnimDuration;
+    public int taskViewTranslationZMinPx;
+    public int taskViewTranslationZIncrementPx;
     public int taskViewRoundedCornerRadiusPx;
     public int searchBarSpaceHeightPx;
 
@@ -108,6 +110,9 @@
                 res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
         taskViewRoundedCornerRadiusPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
+        taskViewTranslationZIncrementPx =
+                res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index 983ad49..c6c29a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -20,10 +20,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.TouchFeedbackDrawable;
 import android.util.AttributeSet;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -151,6 +153,15 @@
         RecentsConfiguration configuration = RecentsConfiguration.getInstance();
         if (Constants.DebugFlags.App.EnableTaskBarThemeColors && t.colorPrimary != 0) {
             setBackgroundColor(t.colorPrimary);
+            // Workaround: The button currently doesn't support setting a custom background tint
+            // not defined in the theme.  Just lower the alpha on the button to make it blend more
+            // into the background.
+            if (mAppInfoButton.getBackground() instanceof TouchFeedbackDrawable) {
+                TouchFeedbackDrawable d = (TouchFeedbackDrawable) mAppInfoButton.getBackground();
+                if (d != null) {
+                    d.setAlpha(96);
+                }
+            }
         } else {
             setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
         }
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 e273ecf..ce43b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -395,12 +395,20 @@
         return false;
     }
 
-    /** Returns whether the specified scroll is out of bounds */
-    boolean isScrollOutOfBounds(int scroll) {
-        return (scroll < mMinScroll) || (scroll > mMaxScroll);
+
+    /** Returns the amount that the scroll is out of bounds */
+    int getScrollAmountOutOfBounds(int scroll) {
+        if (scroll < mMinScroll) {
+            return mMinScroll - scroll;
+        } else if (scroll > mMaxScroll) {
+            return scroll - mMaxScroll;
+        }
+        return 0;
     }
+
+    /** Returns whether the specified scroll is out of bounds */
     boolean isScrollOutOfBounds() {
-        return isScrollOutOfBounds(getStackScroll());
+        return getScrollAmountOutOfBounds(getStackScroll()) != 0;
     }
 
     /** Updates the min and max virtual scroll bounds */
@@ -561,7 +569,7 @@
         int smallestDimension = Math.min(width, height);
         int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
         if (Constants.DebugFlags.App.EnableSearchButton) {
-            // Don't need to pad the top since we have some padding on the search bar already
+            mStackRect.top += padding;
             mStackRect.left += padding;
             mStackRect.right -= padding;
             mStackRect.bottom -= padding;
@@ -1297,9 +1305,13 @@
                 }
                 if (mIsScrolling) {
                     int curStackScroll = mSv.getStackScroll();
-                    if (mSv.isScrollOutOfBounds(curStackScroll + deltaY)) {
-                        // Scale the touch if we are overscrolling
-                        deltaY /= Constants.Values.TaskStackView.TouchOverscrollScaleFactor;
+                    int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
+                    if (overScrollAmount != 0) {
+                        // Bound the overscroll to a fixed amount, and inversely scale the y-movement
+                        // relative to how close we are to the max overscroll
+                        float maxOverScroll = mSv.mTaskRect.height() / 3f;
+                        deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
+                                / maxOverScroll)));
                     }
                     mSv.setStackScroll(curStackScroll + deltaY);
                     if (mSv.isScrollOutOfBounds()) {
@@ -1319,6 +1331,7 @@
                 if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
                     // Enable HW layers on the stack
                     mSv.addHwLayersRefCount("flingScroll");
+                    // XXX: Make this animation a function of the velocity AND distance
                     int overscrollRange = (int) (Math.min(1f,
                             Math.abs((float) velocity / mMaximumVelocity)) *
                             Constants.Values.TaskStackView.TaskStackOverscrollRange);
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 ecd0c45..801de24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -20,6 +20,7 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -33,7 +34,6 @@
 import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
 import com.android.systemui.recents.model.Task;
 
 
@@ -108,6 +108,11 @@
         mRoundedRectClipPath.reset();
         mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
                 radius, radius, Path.Direction.CW);
+
+        // Update the outline
+        Outline o = new Outline();
+        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), radius);
+        setOutline(o);
     }
 
     @Override
@@ -134,14 +139,20 @@
     /** Synchronizes this view's properties with the task's transform */
     void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
                                              TaskViewTransform toTransform, int duration) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        int minZ = config.taskViewTranslationZMinPx;
+        int incZ = config.taskViewTranslationZIncrementPx;
+
         if (duration > 0) {
             if (animateFromTransform != null) {
                 setTranslationY(animateFromTransform.translationY);
+                setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
                 setScaleX(animateFromTransform.scale);
                 setScaleY(animateFromTransform.scale);
                 setAlpha(animateFromTransform.alpha);
             }
             animate().translationY(toTransform.translationY)
+                    .translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)))
                     .scaleX(toTransform.scale)
                     .scaleY(toTransform.scale)
                     .alpha(toTransform.alpha)
@@ -157,6 +168,7 @@
                     .start();
         } else {
             setTranslationY(toTransform.translationY);
+            setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
             setScaleX(toTransform.scale);
             setScaleY(toTransform.scale);
             setAlpha(toTransform.alpha);
@@ -169,6 +181,7 @@
     void resetViewProperties() {
         setTranslationX(0f);
         setTranslationY(0f);
+        setTranslationZ(0f);
         setScaleX(1f);
         setScaleY(1f);
         setAlpha(1f);
@@ -363,7 +376,7 @@
     @Override
     public void onClick(View v) {
         if (v == mInfoView) {
-            // Do nothing
+            hideInfoPane();
         } else if (v == mBarView.mApplicationIcon) {
             mCb.onTaskIconClicked(this);
         } else if (v == mInfoView.mAppInfoButton) {