Add shadow to badges

- Add static method to ShadowGenerator to generate a
  circle Bitmap with a shadow.
- Move setColorScale() from DragView to Themes, and
  use it to apply color to the shadowed Bitmap.

Bug: 35744066
Change-Id: I8d1da528bbf94dc83851f0b4016361ed9f8d5349
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 8d69fe3..8a477d8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 /**
@@ -142,8 +143,8 @@
             mCurrentFilter = new ColorMatrix();
         }
 
-        DragView.setColorScale(getTextColor(), mSrcFilter);
-        DragView.setColorScale(targetColor, mDstFilter);
+        Themes.setColorScaleOnMatrix(getTextColor(), mSrcFilter);
+        Themes.setColorScaleOnMatrix(targetColor, mDstFilter);
         ValueAnimator anim1 = ValueAnimator.ofObject(
                 new FloatArrayEvaluator(mCurrentFilter.getArray()),
                 mSrcFilter.getArray(), mDstFilter.getArray());
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 1f3e22d..5896928 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -20,6 +20,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Shader;
@@ -27,6 +28,7 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.ShadowGenerator;
 
 /**
  * Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
@@ -39,10 +41,12 @@
     private final int mTextHeight;
     private final IconDrawer mLargeIconDrawer;
     private final IconDrawer mSmallIconDrawer;
-    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
+            | Paint.FILTER_BITMAP_FLAG);
+    private final Bitmap mBackgroundWithShadow;
 
-    public BadgeRenderer(Context context) {
+    public BadgeRenderer(final Context context) {
         mContext = context;
         Resources res = context.getResources();
         mSize = res.getDimensionPixelSize(R.dimen.badge_size);
@@ -55,6 +59,8 @@
         Rect tempTextHeight = new Rect();
         mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
         mTextHeight = tempTextHeight.height();
+
+        mBackgroundWithShadow = ShadowGenerator.createCircleWithShadow(Color.WHITE, mSize);
     }
 
     /**
@@ -67,13 +73,15 @@
      */
     public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
             Rect iconBounds, float badgeScale) {
-        mBackgroundPaint.setColor(palette.backgroundColor);
         mTextPaint.setColor(palette.textColor);
         canvas.save(Canvas.MATRIX_SAVE_FLAG);
         // We draw the badge relative to its center.
         canvas.translate(iconBounds.right - mSize / 2, iconBounds.top + mSize / 2);
         canvas.scale(badgeScale, badgeScale);
-        canvas.drawCircle(0, 0, mSize / 2, mBackgroundPaint);
+        mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
+        int backgroundSize = mBackgroundWithShadow.getHeight(); // Same as width.
+        canvas.drawBitmap(mBackgroundWithShadow, -backgroundSize / 2, -backgroundSize / 2,
+                mBackgroundPaint);
         IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
                 ? mLargeIconDrawer : mSmallIconDrawer;
         Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index e4c9be4..7806c98 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
@@ -36,6 +35,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.Arrays;
@@ -259,7 +259,7 @@
             m1.setSaturation(0);
 
             ColorMatrix m2 = new ColorMatrix();
-            setColorScale(color, m2);
+            Themes.setColorScaleOnMatrix(color, m2);
             m1.postConcat(m2);
 
             animateFilterTo(m1.getArray());
@@ -384,11 +384,6 @@
         }
     }
 
-    public static void setColorScale(int color, ColorMatrix target) {
-        target.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
-                Color.blue(color) / 255f, Color.alpha(color) / 255f);
-    }
-
     public int getBlurSizeOutline() {
         return mBlurSizeOutline;
     }
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 23c6a12..cd7cf70 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -19,11 +19,12 @@
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Themes;
 
 /**
@@ -41,12 +42,16 @@
 
     public final int dominantColor;
     public final int backgroundColor;
+    public final ColorMatrixColorFilter backgroundColorMatrixFilter;
     public final int textColor;
     public final int secondaryColor;
 
     private IconPalette(int color) {
         dominantColor = color;
         backgroundColor = getMutedColor(dominantColor);
+        ColorMatrix backgroundColorMatrix = new ColorMatrix();
+        Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
+        backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
         textColor = getTextColorForBackground(backgroundColor);
         secondaryColor = getLowContrastColor(backgroundColor);
     }
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 31276ec..6c603c9 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -83,6 +83,38 @@
         return result;
     }
 
+    public static Bitmap createCircleWithShadow(int circleColor, int diameter) {
+
+        float shadowRadius = diameter * 1f / 32;
+        float shadowYOffset = diameter * 1f / 16;
+
+        int radius = diameter / 2;
+
+        Canvas canvas = new Canvas();
+        Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        blurPaint.setMaskFilter(new BlurMaskFilter(shadowRadius, Blur.NORMAL));
+
+        int center = Math.round(radius + shadowRadius + shadowYOffset);
+        int size = center * 2;
+        Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
+        canvas.setBitmap(result);
+
+        // Draw ambient shadow, center aligned within size
+        blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
+        canvas.drawCircle(center, center, radius, blurPaint);
+
+        // Draw key shadow, bottom aligned within size
+        blurPaint.setAlpha(KEY_SHADOW_ALPHA);
+        canvas.drawCircle(center, center + shadowYOffset, radius, blurPaint);
+
+        // Draw the circle
+        Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        drawPaint.setColor(circleColor);
+        canvas.drawCircle(center, center, radius, drawPaint);
+
+        return result;
+    }
+
     public static ShadowGenerator getInstance(Context context) {
         Preconditions.assertNonUiThread();
         synchronized (LOCK) {
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index acd589e..d863339 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
 import android.view.ContextThemeWrapper;
 
 /**
@@ -49,4 +51,21 @@
         ta.recycle();
         return (int) (255 * alpha + 0.5f);
     }
+
+    /**
+     * Scales a color matrix such that, when applied to color R G B A, it produces R' G' B' A' where
+     * R' = r * R
+     * G' = g * G
+     * B' = b * B
+     * A' = a * A
+     *
+     * The matrix will, for instance, turn white into r g b a, and black will remain black.
+     *
+     * @param color The color r g b a
+     * @param target The ColorMatrix to scale
+     */
+    public static void setColorScaleOnMatrix(int color, ColorMatrix target) {
+        target.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
+                Color.blue(color) / 255f, Color.alpha(color) / 255f);
+    }
 }