Merge "Removed round rect clipping and hardware layers from recents" into lmp-dev
diff --git a/packages/SystemUI/res/drawable/recents_task_view_header_bg_color.xml b/packages/SystemUI/res/drawable/recents_task_view_header_bg_color.xml
new file mode 100644
index 0000000..5f9341c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_task_view_header_bg_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:topLeftRadius="@dimen/recents_task_view_rounded_corners_radius"
+             android:topRightRadius="@dimen/recents_task_view_rounded_corners_radius"/>
+    <solid android:color="#00000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 4cb8498..1b982fb 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -15,10 +15,9 @@
 -->
 <com.android.systemui.recents.views.TaskView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent" 
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:focusable="true"
-    android:background="#FFffffff">
+    android:focusable="true">
     <com.android.systemui.recents.views.TaskViewThumbnail
         android:id="@+id/task_view_thumbnail"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9654da9..ffe52e2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -29,6 +29,10 @@
      ImageView -->
     <bool name="config_recents_thumbnail_image_fits_to_xy">false</bool>
 
+    <!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
+    for devices where the java drawing of round rects may be slow -->
+    <bool name="config_recents_use_hardware_layers">false</bool>
+
     <!-- The number of app thumbnails we keep in memory -->
     <integer name="config_recents_max_thumbnail_count">10</integer>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 2a2caa0..2f5b07b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -119,6 +119,7 @@
     public int launchedToTaskId;
 
     /** Misc **/
+    public boolean useHardwareLayers;
     public int altTabKeyDelay;
 
     /** Dev options and global settings */
@@ -271,6 +272,7 @@
                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
 
         // Misc
+        useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
         altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay);
     }
 
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 e5c06fd..d4b403d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -204,7 +204,8 @@
                     if (!mCancelled) {
                         // Notify that the task data has changed
                         final Drawable newIcon = cachedIcon;
-                        final Bitmap newThumbnail = cachedThumbnail;
+                        final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
+                                ? null : cachedThumbnail;
                         mMainThreadHandler.post(new Runnable() {
                             @Override
                             public void run() {
@@ -252,7 +253,6 @@
 
     BitmapDrawable mDefaultApplicationIcon;
     Bitmap mDefaultThumbnail;
-    Bitmap mLoadingThumbnail;
 
     /** Private Constructor */
     private RecentsTaskLoader(Context context) {
@@ -271,9 +271,6 @@
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         mDefaultThumbnail.setHasAlpha(false);
         mDefaultThumbnail.eraseColor(0xFFffffff);
-        mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-        mLoadingThumbnail.setHasAlpha(false);
-        mLoadingThumbnail.eraseColor(0xFFffffff);
         mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
 
         // Initialize the proxy, cache and loaders
@@ -500,17 +497,16 @@
         // use the default assets in their place until they load
         boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
         applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
-        thumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
         if (requiresLoad) {
             mLoadQueue.addTask(t);
         }
-        t.notifyTaskDataLoaded(thumbnail, applicationIcon);
+        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
     }
 
     /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
         mLoadQueue.removeTask(t);
-        t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
+        t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
     }
 
     /** Completely removes the resource data from the pool. */
@@ -519,7 +515,7 @@
         mThumbnailCache.remove(t.key);
         mApplicationIconCache.remove(t.key);
         if (notifyTaskDataUnloaded) {
-            t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
+            t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index d6889d0..0adc73c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -44,7 +44,6 @@
         mConfig = RecentsConfiguration.getInstance();
         mSourceView = source;
         mCornerRadius = cornerRadius;
-        mSourceView.setClipToOutline(true);
         setClipTop(getClipTop());
         setClipRight(getClipRight());
         setClipBottom(getClipBottom());
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 51adc28..a1ddf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -637,17 +637,31 @@
     /** Returns the current dim. */
     public void setDim(int dim) {
         mDim = dim;
-        // Defer setting hardware layers if we have not yet measured, or there is no dim to draw
-        if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
-            if (mDimAnimator != null) {
-                mDimAnimator.removeAllListeners();
-                mDimAnimator.cancel();
-            }
+        if (mDimAnimator != null) {
+            mDimAnimator.removeAllListeners();
+            mDimAnimator.cancel();
+        }
+        if (mConfig.useHardwareLayers) {
+            // Defer setting hardware layers if we have not yet measured, or there is no dim to draw
+            if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
+                if (mDimAnimator != null) {
+                    mDimAnimator.removeAllListeners();
+                    mDimAnimator.cancel();
+                }
 
-            int inverse = 255 - mDim;
-            mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
-            mLayerPaint.setColorFilter(mDimColorFilter);
-            setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
+                int inverse = 255 - mDim;
+                mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
+                mLayerPaint.setColorFilter(mDimColorFilter);
+                setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
+            }
+        } else {
+            float dimAlpha = mDim / 255.0f;
+            if (mThumbnailView != null) {
+                mThumbnailView.setDimAlpha(dimAlpha);
+            }
+            if (mHeaderView != null) {
+                mHeaderView.setDimAlpha(dim);
+            }
         }
     }
 
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 1743433..fd39126 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -30,10 +30,13 @@
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -58,17 +61,20 @@
     TextView mActivityDescription;
 
     RippleDrawable mBackground;
-    ColorDrawable mBackgroundColor;
+    GradientDrawable mBackgroundColorDrawable;
+    int mBackgroundColor;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
     AnimatorSet mFocusAnimator;
     ValueAnimator backgroundColorAnimator;
+    PorterDuffColorFilter mDimFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
 
     boolean mIsFullscreen;
     boolean mCurrentPrimaryColorIsDark;
     int mCurrentPrimaryColor;
 
     static Paint sHighlightPaint;
+    private Paint mDimPaint = new Paint();
 
     public TaskViewHeader(Context context) {
         this(context, null);
@@ -140,13 +146,14 @@
             }
         }
 
-        mBackgroundColor = new ColorDrawable(0);
+        mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
+                .recents_task_view_header_bg_color);
         // Copy the ripple drawable since we are going to be manipulating it
         mBackground = (RippleDrawable)
                 getContext().getDrawable(R.drawable.recents_task_view_header_bg);
         mBackground = (RippleDrawable) mBackground.mutate().getConstantState().newDrawable();
         mBackground.setColor(ColorStateList.valueOf(0));
-        mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColor);
+        mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColorDrawable);
         setBackground(mBackground);
     }
 
@@ -197,7 +204,8 @@
         int existingBgColor = (getBackground() instanceof ColorDrawable) ?
                 ((ColorDrawable) getBackground()).getColor() : 0;
         if (existingBgColor != t.colorPrimary) {
-            mBackgroundColor.setColor(t.colorPrimary);
+            mBackgroundColorDrawable.setColor(t.colorPrimary);
+            mBackgroundColor = t.colorPrimary;
         }
         mCurrentPrimaryColor = t.colorPrimary;
         mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
@@ -276,7 +284,7 @@
             mBackground.setColor(new ColorStateList(states, colors));
             mBackground.setState(newStates);
             // Pulse the background color
-            int currentColor = mBackgroundColor.getColor();
+            int currentColor = mBackgroundColor;
             int lightPrimaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
             ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
                     lightPrimaryColor, currentColor);
@@ -289,7 +297,9 @@
             backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
-                    mBackgroundColor.setColor((Integer) animation.getAnimatedValue());
+                    int color = (int) animation.getAnimatedValue();
+                    mBackgroundColorDrawable.setColor(color);
+                    mBackgroundColor = color;
                 }
             });
             backgroundColor.setRepeatCount(ValueAnimator.INFINITE);
@@ -307,13 +317,15 @@
         } else {
             if (isRunning) {
                 // Restore the background color
-                int currentColor = mBackgroundColor.getColor();
+                int currentColor = mBackgroundColor;
                 ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
                         currentColor, mCurrentPrimaryColor);
                 backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                     @Override
                     public void onAnimationUpdate(ValueAnimator animation) {
-                        mBackgroundColor.setColor((Integer) animation.getAnimatedValue());
+                        int color = (int) animation.getAnimatedValue();
+                        mBackgroundColorDrawable.setColor(color);
+                        mBackgroundColor = color;
                     }
                 });
                 // Restore the translation
@@ -329,4 +341,11 @@
             }
         }
     }
+
+    public void setDimAlpha(int alpha) {
+        int color = Color.argb(alpha, 0, 0, 0);
+        mDimFilter.setColor(color);
+        mDimPaint.setColorFilter(mDimFilter);
+        setLayerType(LAYER_TYPE_HARDWARE, mDimPaint);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index fe36987..11ca103 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -16,9 +16,20 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LightingColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.View;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -26,12 +37,30 @@
 
 
 /** The task thumbnail view */
-public class TaskViewThumbnail extends FixedSizeImageView {
+public class TaskViewThumbnail extends View {
 
+    private final int mCornerRadius;
+    private final Matrix mScaleMatrix = new Matrix();
     RecentsConfiguration mConfig;
 
     // Task bar clipping
     Rect mClipRect = new Rect();
+    Paint mDrawPaint = new Paint();
+    LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
+    private final RectF mBitmapRect = new RectF();
+    private final RectF mLayoutRect = new RectF();
+    private BitmapShader mBitmapShader;
+    private float mBitmapAlpha;
+    private float mDimAlpha;
+    private ValueAnimator mAlphaAnimator;
+    private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
+            = new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            mBitmapAlpha = (float) animation.getAnimatedValue();
+            updateFilter();
+        }
+    };
 
     public TaskViewThumbnail(Context context) {
         this(context, null);
@@ -48,12 +77,43 @@
     public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
-        setScaleType(ScaleType.FIT_XY);
+        mCornerRadius = mConfig.taskViewRoundedCornerRadiusPx;
+        mDrawPaint.setColorFilter(mLightingColorFilter);
+        mDrawPaint.setFilterBitmap(true);
+        mDrawPaint.setAntiAlias(true);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawRoundRect(0,
+                0,
+                getWidth(),
+                getHeight(),
+                mCornerRadius,
+                mCornerRadius,
+                mDrawPaint);
     }
 
     @Override
     protected void onFinishInflate() {
-        setAlpha(0.9f);
+        mBitmapAlpha = 0.9f;
+        updateFilter();
+    }
+
+    private void updateFilter() {
+        int mul = (int) ((1.0f - mDimAlpha) * mBitmapAlpha * 255);
+        int add = (int) ((1.0f - mDimAlpha) * (1 - mBitmapAlpha) * 255);
+        if (mBitmapShader != null) {
+            mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
+            mLightingColorFilter.setColorAdd(Color.argb(0, add, add, add));
+            mDrawPaint.setColorFilter(mLightingColorFilter);
+            mDrawPaint.setColor(0xffffffff);
+        } else {
+            mDrawPaint.setColorFilter(null);
+            int grey = mul + add;
+            mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
+        }
+        invalidate();
     }
 
     /** Updates the clip rect based on the given task bar. */
@@ -72,11 +132,8 @@
 
     /** Binds the thumbnail view to the screenshot. */
     boolean bindToScreenshot(Bitmap ss) {
-        if (ss != null) {
-            setImageBitmap(ss);
-            return true;
-        }
-        return false;
+        setImageBitmap(ss);
+        return ss != null;
     }
 
     /** Unbinds the thumbnail view from the screenshot. */
@@ -88,12 +145,49 @@
     void rebindToTask(Task t) {
         if (t.thumbnail != null) {
             setImageBitmap(t.thumbnail);
+        } else {
+            setImageBitmap(null);
         }
     }
 
+    public void setImageBitmap(Bitmap bm) {
+        if (bm != null) {
+            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
+                    Shader.TileMode.CLAMP);
+            mDrawPaint.setShader(mBitmapShader);
+            mBitmapRect.set(0, 0, bm.getWidth(), bm.getHeight());
+            updateBitmapScale();
+        } else {
+            mBitmapShader = null;
+            mDrawPaint.setShader(null);
+        }
+        updateFilter();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (changed) {
+            mLayoutRect.set(0, 0, getWidth(), getHeight());
+            updateBitmapScale();
+        }
+    }
+
+    private void updateBitmapScale() {
+        if (mBitmapShader != null) {
+            mScaleMatrix.setRectToRect(mBitmapRect, mLayoutRect, Matrix.ScaleToFit.FILL);
+            mBitmapShader.setLocalMatrix(mScaleMatrix);
+        }
+    }
+
+    public void setDimAlpha(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        updateFilter();
+    }
+
     /** Unbinds the thumbnail view from the task */
     void unbindFromTask() {
-        setImageDrawable(null);
+        setImageBitmap(null);
     }
 
     /** Handles focus changes. */
@@ -112,10 +206,11 @@
     /** Prepares for the enter recents animation. */
     void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask) {
         if (isTaskViewLaunchTargetTask) {
-            setAlpha(1f);
+            mBitmapAlpha = 1f;
         } else {
-            setAlpha(mConfig.taskViewThumbnailAlpha);
+            mBitmapAlpha = mConfig.taskViewThumbnailAlpha;
         }
+        updateFilter();
     }
 
     /** Animates this task thumbnail as it enters recents */
@@ -130,16 +225,32 @@
     }
 
     /** Animates the thumbnail alpha. */
-    void startFadeAnimation(float finalAlpha, int delay, int duration, Runnable postAnimRunnable) {
-        if (postAnimRunnable != null) {
-            animate().withEndAction(postAnimRunnable);
+    void startFadeAnimation(float finalAlpha, int delay, int duration, final Runnable postAnimRunnable) {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
         }
-        animate()
-                .alpha(finalAlpha)
-                .setStartDelay(delay)
-                .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                .setDuration(duration)
-                .withLayer()
-                .start();
+        mAlphaAnimator = ValueAnimator.ofFloat(mBitmapAlpha, finalAlpha);
+        mAlphaAnimator.addUpdateListener(mAlphaUpdateListener);
+        mAlphaAnimator.setStartDelay(delay);
+        mAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+        mAlphaAnimator.setDuration(duration);
+        mAlphaAnimator.start();
+        if (postAnimRunnable != null) {
+            mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+                public boolean mCancelled;
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    mCancelled = true;
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (!mCancelled) {
+                        postAnimRunnable.run();
+                    }
+                }
+            });
+        }
     }
 }