am 6697322a: Merge "Should use Activity.onEnterAnimationComplete. (Bug 18031283)" into lmp-mr1-dev

* commit '6697322af724d44ee59259d4f5563b07c24f62b4':
  Should use Activity.onEnterAnimationComplete. (Bug 18031283)
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c478071..51372276 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -239,7 +239,7 @@
     <dimen name="recents_search_bar_space_height">64dp</dimen>
 
     <!-- The side padding for the task stack as a percentage of the width. -->
-    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.04444</item>
+    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.03333</item>
 
     <!-- The overscroll percentage allowed on the stack. -->
     <item name="recents_stack_overscroll_percentage" format="float" type="dimen">0.0875</item>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 1ca67bc..d66add5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -54,7 +54,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /** A proxy implementation for the recents component */
-public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
+public class AlternateRecentsComponent {
 
     final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
     final public static String EXTRA_FROM_SEARCH_HOME = "recents.triggeredOverSearchHome";
@@ -63,7 +63,6 @@
     final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
     final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey";
 
-    final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
     final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
     final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
 
@@ -78,9 +77,7 @@
     Context mContext;
     LayoutInflater mInflater;
     SystemServicesProxy mSystemServicesProxy;
-    Handler mHandler;
     boolean mBootCompleted;
-    boolean mStartAnimationTriggered;
 
     // Task launching
     RecentsConfiguration mConfig;
@@ -106,7 +103,6 @@
         mInflater = LayoutInflater.from(context);
         mContext = context;
         mSystemServicesProxy = new SystemServicesProxy(context);
-        mHandler = new Handler();
         mTaskStackBounds = new Rect();
     }
 
@@ -364,30 +360,27 @@
      * Creates the activity options for a unknown state->recents transition.
      */
     ActivityOptions getUnknownTransitionActivityOptions() {
-        mStartAnimationTriggered = false;
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_unknown_enter,
-                R.anim.recents_from_unknown_exit, mHandler, this);
+                R.anim.recents_from_unknown_exit);
     }
 
     /**
      * Creates the activity options for a home->recents transition.
      */
     ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
-        mStartAnimationTriggered = false;
         if (fromSearchHome) {
             return ActivityOptions.makeCustomAnimation(mContext,
                     R.anim.recents_from_search_launcher_enter,
-                    R.anim.recents_from_search_launcher_exit, mHandler, this);
+                    R.anim.recents_from_search_launcher_exit);
         }
         return ActivityOptions.makeCustomAnimation(mContext,
                 R.anim.recents_from_launcher_enter,
-                R.anim.recents_from_launcher_exit, mHandler, this);
+                R.anim.recents_from_launcher_exit);
     }
 
     /**
-     * Creates the activity options for an app->recents transition.  If this method sets the static
-     * screenshot, then we will use that for the transition.
+     * Creates the activity options for an app->recents transition.
      */
     ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
             boolean isTopTaskHome) {
@@ -411,10 +404,9 @@
                 c.setBitmap(null);
             }
 
-            mStartAnimationTriggered = false;
             return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mStatusBarView,
                     thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
-                    toTaskRect.height(), this);
+                    toTaskRect.height(), null);
         }
 
         // If both the screenshot and thumbnail fails, then just fall back to the default transition
@@ -551,42 +543,4 @@
             sRecentsComponentCallbacks.onVisibilityChanged(visible);
         }
     }
-
-    /**** OnAnimationStartedListener Implementation ****/
-
-    @Override
-    public void onAnimationStarted() {
-        // Notify recents to start the enter animation
-        if (!mStartAnimationTriggered) {
-            // There can be a race condition between the start animation callback and
-            // the start of the new activity (where we register the receiver that listens
-            // to this broadcast, so we add our own receiver and if that gets called, then
-            // we know the activity has not yet started and we can retry sending the broadcast.
-            BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (getResultCode() == Activity.RESULT_OK) {
-                        mStartAnimationTriggered = true;
-                        return;
-                    }
-
-                    // Schedule for the broadcast to be sent again after some time
-                    mHandler.postDelayed(new Runnable() {
-                        @Override
-                        public void run() {
-                            onAnimationStarted();
-                        }
-                    }, 75);
-                }
-            };
-
-            // Send the broadcast to notify Recents that the animation has started
-            Intent intent = new Intent(ACTION_START_ENTER_ANIMATION);
-            intent.setPackage(mContext.getPackageName());
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
-                    Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
-                    fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 1c8f55b..2b55c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -141,14 +141,6 @@
             } else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
                 // If we are toggling Recents, then first unfilter any filtered stacks first
                 dismissRecentsToFocusedTaskOrHome(true);
-            } else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) {
-                // Try and start the enter animation (or restart it on configuration changed)
-                ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
-                mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
-                onEnterAnimationTriggered();
-                // Notify the fallback receiver that we have successfully got the broadcast
-                // See AlternateRecentsComponent.onAnimationStarted()
-                setResultCode(Activity.RESULT_OK);
             }
         }
     };
@@ -441,7 +433,8 @@
         // Try and start the enter animation (or restart it on configuration changed)
         ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
         mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
-        onEnterAnimationTriggered();
+        // Animate the SystemUI scrim views
+        mScrimViews.startEnterRecentsAnimation();
     }
 
     @Override
@@ -469,7 +462,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY);
         filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY);
-        filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register any broadcast receivers for the task loader
@@ -512,6 +504,16 @@
     }
 
     @Override
+    public void onEnterAnimationComplete() {
+        // Try and start the enter animation (or restart it on configuration changed)
+        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
+        mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
+
+        // Animate the SystemUI scrim views
+        mScrimViews.startEnterRecentsAnimation();
+    }
+
+    @Override
     public void onTrimMemory(int level) {
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         if (loader != null) {
@@ -592,12 +594,6 @@
         }
     }
 
-    /** Called when the enter recents animation is triggered. */
-    public void onEnterAnimationTriggered() {
-        // Animate the SystemUI scrim views
-        mScrimViews.startEnterRecentsAnimation();
-    }
-
     /**** RecentsView.RecentsViewCallbacks Implementation ****/
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index f01d17c..a0dee07 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.misc;
 
+import android.animation.Animator;
 import android.content.Intent;
 import android.graphics.Color;
 import android.graphics.Matrix;
@@ -188,4 +189,15 @@
         int flags = intent.getFlags();
         return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
     }
+
+    /**
+     * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
+     * are not called.
+     */
+    public static void cancelAnimationWithoutCallbacks(Animator animator) {
+        if (animator != null) {
+            animator.removeAllListeners();
+            animator.cancel();
+        }
+    }
 }
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 421b905..5f8f3f2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -68,7 +68,7 @@
             mSourceView.invalidateOutline();
             updateClipBounds();
             if (!mConfig.useHardwareLayers) {
-                mSourceView.mThumbnailView.updateVisibility(
+                mSourceView.mThumbnailView.updateThumbnailVisibility(
                         bottom - mSourceView.getPaddingBottom());
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index c9113fe..04f7c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.widget.OverScroller;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.Utilities;
 
 /* The scrolling logic for a TaskStackView */
 public class TaskStackViewScroller {
@@ -161,10 +162,7 @@
 
     /** Aborts any current stack scrolls */
     void stopBoundScrollAnimation() {
-        if (mScrollAnimator != null) {
-            mScrollAnimator.removeAllListeners();
-            mScrollAnimator.cancel();
-        }
+        Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
     }
 
     /**** OverScroller ****/
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 dfb30f3..7b4e10a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -28,10 +28,9 @@
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.AlternateRecentsComponent;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
@@ -53,11 +52,11 @@
 
     float mTaskProgress;
     ObjectAnimator mTaskProgressAnimator;
-    ObjectAnimator mDimAnimator;
     float mMaxDimScale;
-    int mDim;
+    int mDimAlpha;
     AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1f);
-    PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
+    PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
+    Paint mDimLayerPaint = new Paint();
 
     Task mTask;
     boolean mTaskDataLoaded;
@@ -65,7 +64,6 @@
     boolean mFocusAnimationsEnabled;
     boolean mClipViewInStack;
     AnimateableViewBounds mViewBounds;
-    Paint mLayerPaint = new Paint();
 
     View mContent;
     TaskViewThumbnail mThumbnailView;
@@ -130,7 +128,7 @@
         mContent = findViewById(R.id.task_view_content);
         mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar);
         mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail);
-        mThumbnailView.enableTaskBarClip(mHeaderView);
+        mThumbnailView.updateClipToTaskBar(mHeaderView);
         mActionButtonView = findViewById(R.id.lock_to_app_fab);
         mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
             @Override
@@ -179,10 +177,7 @@
                 !mConfig.fakeShadows, updateCallback);
 
         // Update the task progress
-        if (mTaskProgressAnimator != null) {
-            mTaskProgressAnimator.removeAllListeners();
-            mTaskProgressAnimator.cancel();
-        }
+        Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
         if (duration <= 0) {
             setTaskProgress(toTransform.p);
         } else {
@@ -377,7 +372,7 @@
             mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);
 
             // Animate the dim
-            if (mDim > 0) {
+            if (mDimAlpha > 0) {
                 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
                 anim.setDuration(mConfig.taskBarExitAnimDuration);
                 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
@@ -495,26 +490,16 @@
 
     /** Returns the current dim. */
     public void setDim(int dim) {
-        mDim = dim;
-        if (mDimAnimator != null) {
-            mDimAnimator.removeAllListeners();
-            mDimAnimator.cancel();
-        }
+        mDimAlpha = dim;
         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);
-                mContent.setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
+                mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0));
+                mDimLayerPaint.setColorFilter(mDimColorFilter);
+                mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
             }
         } else {
-            float dimAlpha = mDim / 255.0f;
+            float dimAlpha = mDimAlpha / 255.0f;
             if (mThumbnailView != null) {
                 mThumbnailView.setDimAlpha(dimAlpha);
             }
@@ -526,7 +511,7 @@
 
     /** Returns the current dim. */
     public int getDim() {
-        return mDim;
+        return mDimAlpha;
     }
 
     /** Animates the dim to the task progress. */
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 6554f82..ba868f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -36,7 +36,6 @@
 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;
@@ -56,23 +55,27 @@
 
     RecentsConfiguration mConfig;
 
+    // Header views
     ImageView mDismissButton;
     ImageView mApplicationIcon;
     TextView mActivityDescription;
 
-    RippleDrawable mBackground;
-    GradientDrawable mBackgroundColorDrawable;
+    // Header drawables
+    boolean mCurrentPrimaryColorIsDark;
+    int mCurrentPrimaryColor;
     int mBackgroundColor;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
+    RippleDrawable mBackground;
+    GradientDrawable mBackgroundColorDrawable;
     AnimatorSet mFocusAnimator;
-    PorterDuffColorFilter mDimFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
 
-    boolean mCurrentPrimaryColorIsDark;
-    int mCurrentPrimaryColor;
-
+    // Static highlight that we draw at the top of each view
     static Paint sHighlightPaint;
-    private Paint mDimPaint = new Paint();
+
+    // Header dim, which is only used when task view hardware layers are not used
+    Paint mDimLayerPaint = new Paint();
+    PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
 
     public TaskViewHeader(Context context) {
         this(context, null);
@@ -172,6 +175,16 @@
         return false;
     }
 
+    /**
+     * Sets the dim alpha, only used when we are not using hardware layers.
+     * (see RecentsConfiguration.useHardwareLayers)
+     */
+    void setDimAlpha(int alpha) {
+        mDimColorFilter.setColor(Color.argb(alpha, 0, 0, 0));
+        mDimLayerPaint.setColorFilter(mDimColorFilter);
+        setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
+    }
+
     /** Returns the secondary color for a primary color. */
     int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
         int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
@@ -266,8 +279,7 @@
         boolean isRunning = false;
         if (mFocusAnimator != null) {
             isRunning = mFocusAnimator.isRunning();
-            mFocusAnimator.removeAllListeners();
-            mFocusAnimator.cancel();
+            Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
         }
 
         if (focused) {
@@ -344,11 +356,4 @@
             }
         }
     }
-
-    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 a946a84..c83248e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -33,37 +33,48 @@
 import android.util.AttributeSet;
 import android.view.View;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 
 
-/** The task thumbnail view */
+/**
+ * The task thumbnail view.  It implements an image view that allows for animating the dim and
+ * alpha of the thumbnail image.
+ */
 public class TaskViewThumbnail extends View {
 
-    private final int mCornerRadius;
-    private final Matrix mScaleMatrix = new Matrix();
     RecentsConfiguration mConfig;
 
-    // Task bar clipping
-    Rect mClipRect = new Rect();
+    // Drawing
+    float mDimAlpha;
+    Matrix mScaleMatrix = new Matrix();
     Paint mDrawPaint = new Paint();
+    RectF mBitmapRect = new RectF();
+    RectF mLayoutRect = new RectF();
+    BitmapShader mBitmapShader;
     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 View mTaskBar;
-    private boolean mInvisible;
-    private ValueAnimator mAlphaAnimator;
-    private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
+
+    // Thumbnail alpha
+    float mThumbnailAlpha;
+    ValueAnimator mThumbnailAlphaAnimator;
+    ValueAnimator.AnimatorUpdateListener mThumbnailAlphaUpdateListener
             = new ValueAnimator.AnimatorUpdateListener() {
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
-            mBitmapAlpha = (float) animation.getAnimatedValue();
-            updateFilter();
+            mThumbnailAlpha = (float) animation.getAnimatedValue();
+            updateThumbnailPaintFilter();
         }
     };
 
+    // Task bar clipping, the top of this thumbnail can be clipped against the opaque header
+    // bar that overlaps this thumbnail
+    View mTaskBar;
+    Rect mClipRect = new Rect();
+
+    // Visibility optimization, if the thumbnail height is less than the height of the header
+    // bar for the task view, then just mark this thumbnail view as invisible
+    boolean mInvisible;
+
     public TaskViewThumbnail(Context context) {
         this(context, null);
     }
@@ -79,103 +90,15 @@
     public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
-        mCornerRadius = mConfig.taskViewRoundedCornerRadiusPx;
         mDrawPaint.setColorFilter(mLightingColorFilter);
         mDrawPaint.setFilterBitmap(true);
         mDrawPaint.setAntiAlias(true);
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
-        if (mInvisible) {
-            return;
-        }
-        canvas.drawRoundRect(0,
-                0,
-                getWidth(),
-                getHeight(),
-                mCornerRadius,
-                mCornerRadius,
-                mDrawPaint);
-    }
-
-    @Override
     protected void onFinishInflate() {
-        mBitmapAlpha = 0.9f;
-        updateFilter();
-    }
-
-    private void updateFilter() {
-        if (mInvisible) {
-            return;
-        }
-        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. */
-    void enableTaskBarClip(View taskBar) {
-        mTaskBar = taskBar;
-        int top = (int) Math.max(0, taskBar.getTranslationY() +
-                taskBar.getMeasuredHeight() - 1);
-        mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
-        setClipBounds(mClipRect);
-    }
-
-    void updateVisibility(int clipBottom) {
-        boolean invisible = mTaskBar != null && getHeight() - clipBottom < mTaskBar.getHeight();
-        if (invisible != mInvisible) {
-            mInvisible = invisible;
-            if (!mInvisible) {
-                updateFilter();
-            }
-            invalidate();
-        }
-    }
-
-    /** Binds the thumbnail view to the screenshot. */
-    boolean bindToScreenshot(Bitmap ss) {
-        setImageBitmap(ss);
-        return ss != null;
-    }
-
-    /** Unbinds the thumbnail view from the screenshot. */
-    void unbindFromScreenshot() {
-        setImageBitmap(null);
-    }
-
-    /** Binds the thumbnail view to the task */
-    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();
+        mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+        updateThumbnailPaintFilter();
     }
 
     @Override
@@ -183,25 +106,106 @@
         super.onLayout(changed, left, top, right, bottom);
         if (changed) {
             mLayoutRect.set(0, 0, getWidth(), getHeight());
-            updateBitmapScale();
+            updateThumbnailScale();
         }
     }
 
-    private void updateBitmapScale() {
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mInvisible) {
+            return;
+        }
+        // Draw the thumbnail with the rounded corners
+        canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
+                mConfig.taskViewRoundedCornerRadiusPx,
+                mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint);
+    }
+
+    /** Sets the thumbnail to a given bitmap. */
+    void setThumbnail(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());
+            updateThumbnailScale();
+        } else {
+            mBitmapShader = null;
+            mDrawPaint.setShader(null);
+        }
+        updateThumbnailPaintFilter();
+    }
+
+    /** Updates the paint to draw the thumbnail. */
+    void updateThumbnailPaintFilter() {
+        if (mInvisible) {
+            return;
+        }
+        int mul = (int) ((1.0f - mDimAlpha) * mThumbnailAlpha * 255);
+        int add = (int) ((1.0f - mDimAlpha) * (1 - mThumbnailAlpha) * 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 {
+            int grey = mul + add;
+            mDrawPaint.setColorFilter(null);
+            mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
+        }
+        invalidate();
+    }
+
+    /** Updates the thumbnail shader's scale transform. */
+    void updateThumbnailScale() {
         if (mBitmapShader != null) {
             mScaleMatrix.setRectToRect(mBitmapRect, mLayoutRect, Matrix.ScaleToFit.FILL);
             mBitmapShader.setLocalMatrix(mScaleMatrix);
         }
     }
 
+    /** Updates the clip rect based on the given task bar. */
+    void updateClipToTaskBar(View taskBar) {
+        mTaskBar = taskBar;
+        int top = (int) Math.max(0, taskBar.getTranslationY() +
+                taskBar.getMeasuredHeight() - 1);
+        mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
+        setClipBounds(mClipRect);
+    }
+
+    /** Updates the visibility of the the thumbnail. */
+    void updateThumbnailVisibility(int clipBottom) {
+        boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
+        if (invisible != mInvisible) {
+            mInvisible = invisible;
+            if (!mInvisible) {
+                updateThumbnailPaintFilter();
+            }
+            invalidate();
+        }
+    }
+
+    /**
+     * Sets the dim alpha, only used when we are not using hardware layers.
+     * (see RecentsConfiguration.useHardwareLayers)
+     */
     public void setDimAlpha(float dimAlpha) {
         mDimAlpha = dimAlpha;
-        updateFilter();
+        updateThumbnailPaintFilter();
+    }
+
+    /** Binds the thumbnail view to the task */
+    void rebindToTask(Task t) {
+        if (t.thumbnail != null) {
+            setThumbnail(t.thumbnail);
+        } else {
+            setThumbnail(null);
+        }
     }
 
     /** Unbinds the thumbnail view from the task */
     void unbindFromTask() {
-        setImageBitmap(null);
+        setThumbnail(null);
     }
 
     /** Handles focus changes. */
@@ -217,54 +221,46 @@
         }
     }
 
-    /** Prepares for the enter recents animation. */
+    /**
+     * Prepares for the enter recents animation, this gets called before the the view
+     * is first visible and will be followed by a startEnterRecentsAnimation() call.
+     */
     void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask) {
         if (isTaskViewLaunchTargetTask) {
-            mBitmapAlpha = 1f;
+            mThumbnailAlpha = 1f;
         } else {
-            mBitmapAlpha = mConfig.taskViewThumbnailAlpha;
+            mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
         }
-        updateFilter();
+        updateThumbnailPaintFilter();
     }
 
-    /** Animates this task thumbnail as it enters recents */
+    /** Animates this task thumbnail as it enters Recents. */
     void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
         startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay,
                 mConfig.taskBarEnterAnimDuration, postAnimRunnable);
     }
 
-    /** Animates this task thumbnail as it exits recents */
+    /** Animates this task thumbnail as it exits Recents. */
     void startLaunchTaskAnimation(Runnable postAnimRunnable) {
         startFadeAnimation(1f, 0, mConfig.taskBarExitAnimDuration, postAnimRunnable);
     }
 
-    /** Animates the thumbnail alpha. */
+    /** Starts a new thumbnail alpha animation. */
     void startFadeAnimation(float finalAlpha, int delay, int duration, final Runnable postAnimRunnable) {
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.cancel();
-        }
-        mAlphaAnimator = ValueAnimator.ofFloat(mBitmapAlpha, finalAlpha);
-        mAlphaAnimator.addUpdateListener(mAlphaUpdateListener);
-        mAlphaAnimator.setStartDelay(delay);
-        mAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
-        mAlphaAnimator.setDuration(duration);
-        mAlphaAnimator.start();
+        Utilities.cancelAnimationWithoutCallbacks(mThumbnailAlphaAnimator);
+        mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
+        mThumbnailAlphaAnimator.setStartDelay(delay);
+        mThumbnailAlphaAnimator.setDuration(duration);
+        mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+        mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
         if (postAnimRunnable != null) {
-            mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
-                public boolean mCancelled;
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    mCancelled = true;
-                }
-
+            mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    if (!mCancelled) {
-                        postAnimRunnable.run();
-                    }
+                    postAnimRunnable.run();
                 }
             });
         }
+        mThumbnailAlphaAnimator.start();
     }
 }