Separating double shadow logic for BubbleTextView in a separate subclass

This allows better customization and reuse of the double shadow logic and simplified
various attribute management

Change-Id: I5e277d8399756385452d8bb8c0a0107234a76d34
diff --git a/res/layout/app_icon.xml b/res/layout/app_icon.xml
index fa6eb89..52df694 100644
--- a/res/layout/app_icon.xml
+++ b/res/layout/app_icon.xml
@@ -14,4 +14,4 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.BubbleTextView style="@style/BaseIcon.Workspace" />
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" />
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index ccc6b01..4093744 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -20,7 +20,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:focusable="true" >
-    <com.android.launcher3.BubbleTextView
+    <com.android.launcher3.views.DoubleShadowBubbleTextView
         style="@style/BaseIcon.Workspace"
         android:id="@+id/folder_icon_name"
         android:focusable="false"
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 1062269..4cd03ce 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -42,7 +42,6 @@
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="16sp"
         android:textAlignment="viewStart"
-        launcher:customShadows="false"
         launcher:deferShadowGeneration="true"
         launcher:iconDisplay="widget_section"
         launcher:iconSizeOverride="@dimen/widget_section_icon_size"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 27992ce..05e66ad 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -43,10 +43,15 @@
             <enum name="shortcut_popup" value="4" />
         </attr>
         <attr name="deferShadowGeneration" format="boolean" />
-        <attr name="customShadows" format="boolean" />
         <attr name="centerVertically" format="boolean" />
+    </declare-styleable>
+
+    <declare-styleable name="ShadowInfo">
         <attr name="ambientShadowColor" format="color" />
+        <attr name="ambientShadowBlur" format="dimension" />
         <attr name="keyShadowColor" format="color" />
+        <attr name="keyShadowBlur" format="dimension" />
+        <attr name="keyShadowOffset" format="dimension" />
     </declare-styleable>
 
     <!-- PagedView specific attributes. These attributes are used to customize
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b982136..8943a45 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -119,16 +119,17 @@
 
         <!-- No shadows in the base theme -->
         <item name="android:shadowRadius">0</item>
-        <item name="customShadows">false</item>
     </style>
 
     <!-- Icon displayed on the worksapce -->
     <style name="BaseIcon.Workspace">
-        <item name="customShadows">true</item>
         <item name="android:shadowRadius">2.0</item>
         <item name="android:shadowColor">?attr/workspaceShadowColor</item>
-        <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
         <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
+        <item name="ambientShadowBlur">2.5dp</item>
+        <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
+        <item name="keyShadowBlur">1dp</item>
+        <item name="keyShadowOffset">.5dp</item>
     </style>
 
     <!-- Theme for the popup container -->
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index f8e87c5..2306b1b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -27,7 +27,6 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.support.v4.graphics.ColorUtils;
@@ -62,11 +61,6 @@
  */
 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
 
-    // Dimensions in DP
-    private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
-    private static final float KEY_SHADOW_RADIUS = 1f;
-    private static final float KEY_SHADOW_OFFSET = 0.5f;
-
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
@@ -76,22 +70,15 @@
     private final Launcher mLauncher;
     private Drawable mIcon;
     private final boolean mCenterVertically;
-    private final Drawable mBackground;
     private OnLongClickListener mOnLongClickListener;
     private final CheckLongPressHelper mLongPressHelper;
     private final HolographicOutlineHelper mOutlineHelper;
     private final StylusEventHelper mStylusEventHelper;
-    private final int mAmbientShadowColor;
-    private final int mKeyShadowColor;
-
-    private boolean mBackgroundSizeChanged;
+    private final float mSlop;
 
     private Bitmap mPressedBackground;
 
-    private float mSlop;
-
     private final boolean mDeferShadowGenerationOnTouch;
-    private final boolean mCustomShadowsEnabled;
     private final boolean mLayoutHorizontal;
     private final int mIconSize;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -154,15 +141,13 @@
         super(context, attrs, defStyle);
         mLauncher = Launcher.getLauncher(context);
         DeviceProfile grid = mLauncher.getDeviceProfile();
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
-        mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, false);
         mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
         mDeferShadowGenerationOnTouch =
                 a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
-        mAmbientShadowColor = a.getColor(R.styleable.BubbleTextView_ambientShadowColor, 0x33000000);
-        mKeyShadowColor = a.getColor(R.styleable.BubbleTextView_keyShadowColor, 0x66000000);
 
         int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
         int defaultIconSize = grid.iconSizePx;
@@ -184,23 +169,12 @@
                 defaultIconSize);
         a.recycle();
 
-        if (mCustomShadowsEnabled) {
-            // Draw the background itself as the parent is drawn twice.
-            mBackground = getBackground();
-            setBackground(null);
-
-            // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
-            float density = getResources().getDisplayMetrics().density;
-            setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, mAmbientShadowColor);
-        } else {
-            mBackground = null;
-        }
-
         mLongPressHelper = new CheckLongPressHelper(this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
         setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info) {
@@ -262,19 +236,6 @@
     }
 
     @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
-            mBackgroundSizeChanged = true;
-        }
-        return super.setFrame(left, top, right, bottom);
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mBackground || super.verifyDrawable(who);
-    }
-
-    @Override
     public void setTag(Object tag) {
         if (tag != null) {
             LauncherModel.checkItemInfo((ItemInfo) tag);
@@ -415,54 +376,14 @@
         return result;
     }
 
+    @SuppressWarnings("wrongcall")
+    protected void drawWithoutBadge(Canvas canvas) {
+        super.onDraw(canvas);
+    }
+
     @Override
-    public void draw(Canvas canvas) {
-        if (!mCustomShadowsEnabled) {
-            super.draw(canvas);
-            drawBadgeIfNecessary(canvas);
-            return;
-        }
-
-        final Drawable background = mBackground;
-        if (background != null) {
-            final int scrollX = getScrollX();
-            final int scrollY = getScrollY();
-
-            if (mBackgroundSizeChanged) {
-                background.setBounds(0, 0,  getRight() - getLeft(), getBottom() - getTop());
-                mBackgroundSizeChanged = false;
-            }
-
-            if ((scrollX | scrollY) == 0) {
-                background.draw(canvas);
-            } else {
-                canvas.translate(scrollX, scrollY);
-                background.draw(canvas);
-                canvas.translate(-scrollX, -scrollY);
-            }
-        }
-
-        // If text is transparent, don't draw any shadow
-        if ((getCurrentTextColor() >> 24) == 0) {
-            getPaint().clearShadowLayer();
-            super.draw(canvas);
-            drawBadgeIfNecessary(canvas);
-            return;
-        }
-
-        // We enhance the shadow by drawing the shadow twice
-        float density = getResources().getDisplayMetrics().density;
-        getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, mAmbientShadowColor);
-        super.draw(canvas);
-        canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
-                getScrollX() + getWidth(),
-                getScrollY() + getHeight(), Region.Op.INTERSECT);
-        getPaint().setShadowLayer(
-                density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, mKeyShadowColor);
-        super.draw(canvas);
-        canvas.restore();
-
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
         drawBadgeIfNecessary(canvas);
     }
 
@@ -470,7 +391,7 @@
      * Draws the icon badge in the top right corner of the icon bounds.
      * @param canvas The canvas to draw to.
      */
-    private void drawBadgeIfNecessary(Canvas canvas) {
+    protected void drawBadgeIfNecessary(Canvas canvas) {
         if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
             getIconBounds(mTempIconBounds);
             mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
@@ -509,14 +430,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (mBackground != null) mBackground.setCallback(this);
-        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mCenterVertically) {
             Paint.FontMetrics fm = getPaint().getFontMetrics();
@@ -530,12 +443,6 @@
     }
 
     @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mBackground != null) mBackground.setCallback(null);
-    }
-
-    @Override
     public void setTextColor(int color) {
         mTextColor = color;
         super.setTextColor(color);
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
new file mode 100644
index 0000000..9c8457a
--- /dev/null
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Region;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+
+/**
+ * Extension of {@link BubbleTextView} which draws two shadows on the text (ambient and key shadows}
+ */
+public class DoubleShadowBubbleTextView extends BubbleTextView {
+
+    private final ShadowInfo mShadowInfo;
+
+    public DoubleShadowBubbleTextView(Context context) {
+        this(context, null);
+    }
+
+    public DoubleShadowBubbleTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DoubleShadowBubbleTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mShadowInfo = new ShadowInfo(context, attrs, defStyle);
+        setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
+    }
+    @Override
+    public void onDraw(Canvas canvas) {
+        // If text is transparent, don't draw any shadow
+        if ((getCurrentTextColor() >> 24) == 0) {
+            getPaint().clearShadowLayer();
+            super.onDraw(canvas);
+            return;
+        }
+
+        // We enhance the shadow by drawing the shadow twice
+        getPaint().setShadowLayer(
+                mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
+
+        drawWithoutBadge(canvas);
+        canvas.save(Canvas.CLIP_SAVE_FLAG);
+        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
+                getScrollX() + getWidth(),
+                getScrollY() + getHeight(), Region.Op.INTERSECT);
+
+        getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f,
+                mShadowInfo.keyShadowOffset, mShadowInfo.keyShadowColor);
+        drawWithoutBadge(canvas);
+        canvas.restore();
+
+        drawBadgeIfNecessary(canvas);
+    }
+
+    public static class ShadowInfo {
+        public final float ambientShadowBlur;
+        public final int ambientShadowColor;
+
+        public final float keyShadowBlur;
+        public final float keyShadowOffset;
+        public final int keyShadowColor;
+
+        public ShadowInfo(Context c, AttributeSet attrs, int defStyle) {
+
+            TypedArray a = c.obtainStyledAttributes(
+                    attrs, R.styleable.ShadowInfo, defStyle, 0);
+
+            ambientShadowBlur = a.getDimension(R.styleable.ShadowInfo_ambientShadowBlur, 0);
+            ambientShadowColor = a.getColor(R.styleable.ShadowInfo_ambientShadowColor, 0);
+
+            keyShadowBlur = a.getDimension(R.styleable.ShadowInfo_keyShadowBlur, 0);
+            keyShadowOffset = a.getDimension(R.styleable.ShadowInfo_keyShadowOffset, 0);
+            keyShadowColor = a.getColor(R.styleable.ShadowInfo_keyShadowColor, 0);
+            a.recycle();
+        }
+    }
+}