Only create one bitmap to clip popup item corners

Previously we created a bitmap the size of the entire popup item and
drew a rounded rect in it to clip the corners. Now we create a bitmap
that is only the size of the rounded corner radius, and rotate and
translate it to all four corners to achieve the same clipping.

Besides the reduced memory of this approach, it also simplifies things
when we reduce the item's height (e.g. when the second to last
notification is dismissed), because we no longer have to recreate the
background bitmap to match the new size; the translation calculations
in dispatchDraw() automatically take the current size into account.

Bug: 35869307
Change-Id: I938bbc4bd87479580287426b4197516caf1a64d4
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index c6268e2..37b8325 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.notification;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -74,20 +73,8 @@
 
     public Animator animateHeightRemoval(int heightToRemove) {
         final int newHeight = getHeight() - heightToRemove;
-        Animator heightAnimator = new PillHeightRevealOutlineProvider(mPillRect,
+        return new PillHeightRevealOutlineProvider(mPillRect,
                 getBackgroundRadius(), newHeight).createRevealAnimator(this, true /* isReversed */);
-        heightAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (newHeight > 0) {
-                    measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
-                            MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
-                    initializeBackgroundClipping(true /* force */);
-                    invalidate();
-                }
-            }
-        });
-        return heightAnimator;
     }
 
     public void updateHeader(int notificationCount) {
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index 71daae9..0853c13 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -21,14 +21,13 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -52,8 +51,9 @@
 
     protected View mIconView;
 
-    private final Paint mBackgroundClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
-            Paint.FILTER_BITMAP_FLAG);
+    private final Paint mBackgroundClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+    private final Matrix mMatrix = new Matrix();
+    private Bitmap mRoundedCornerBitmap;
 
     public PopupItemView(Context context) {
         this(context, null, 0);
@@ -67,6 +67,14 @@
         super(context, attrs, defStyle);
 
         mPillRect = new Rect();
+
+        // Initialize corner clipping Bitmap and Paint.
+        int radius = (int) getBackgroundRadius();
+        mRoundedCornerBitmap = Bitmap.createBitmap(radius, radius, Bitmap.Config.ALPHA_8);
+        Canvas canvas = new Canvas();
+        canvas.setBitmap(mRoundedCornerBitmap);
+        canvas.drawArc(0, 0, radius*2, radius*2, 180, 90, true, mBackgroundClipPaint);
+        mBackgroundClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
     }
 
     @Override
@@ -81,30 +89,29 @@
         mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
     }
 
-    protected void initializeBackgroundClipping(boolean force) {
-        if (force || mBackgroundClipPaint.getShader() == null) {
-            mBackgroundClipPaint.setXfermode(null);
-            mBackgroundClipPaint.setShader(null);
-            Bitmap backgroundBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
-                    Bitmap.Config.ALPHA_8);
-            Canvas canvas = new Canvas();
-            canvas.setBitmap(backgroundBitmap);
-            canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
-                    getBackgroundRadius(), getBackgroundRadius(), mBackgroundClipPaint);
-            Shader backgroundClipShader = new BitmapShader(backgroundBitmap,
-                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
-            mBackgroundClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
-            mBackgroundClipPaint.setShader(backgroundClipShader);
-        }
-    }
-
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        initializeBackgroundClipping(false /* force */);
-        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
-                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
         super.dispatchDraw(canvas);
-        canvas.drawPaint(mBackgroundClipPaint);
+
+        int cornerWidth = mRoundedCornerBitmap.getWidth();
+        int cornerHeight = mRoundedCornerBitmap.getHeight();
+        // Clip top left corner.
+        mMatrix.reset();
+        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+        // Clip top right corner.
+        mMatrix.setRotate(90, cornerWidth / 2, cornerHeight / 2);
+        mMatrix.postTranslate(canvas.getWidth() - cornerWidth, 0);
+        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+        // Clip bottom right corner.
+        mMatrix.setRotate(180, cornerWidth / 2, cornerHeight / 2);
+        mMatrix.postTranslate(canvas.getWidth() - cornerWidth, canvas.getHeight() - cornerHeight);
+        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+        // Clip bottom left corner.
+        mMatrix.setRotate(270, cornerWidth / 2, cornerHeight / 2);
+        mMatrix.postTranslate(0, canvas.getHeight() - cornerHeight);
+        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+
         canvas.restoreToCount(saveCount);
     }