Changing the FolderIcon shape based on AdpativeIcon

> Creating an abstract interface to represent a folderIcon shape
> Defined few common folder shapes
> Picking the folder shape closest to the AdaptiveIcon shape

Bug: 111433118
Change-Id: Ib35eddbdd6692767aa9dbe6aae1a379a68cc456a
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index c4d1058..94c8d45 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -26,6 +26,8 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Path;
 import android.graphics.Rect;
 import android.text.InputType;
 import android.text.Selection;
@@ -151,6 +153,8 @@
     // Cell ranks used for drag and drop
     @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
 
+    private Path mClipPath;
+
     @ViewDebug.ExportedProperty(category = "launcher",
             mapping = {
                     @ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"),
@@ -1476,4 +1480,25 @@
             sHintText = res.getString(R.string.folder_hint_text);
         }
     }
+
+    /**
+     * Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
+     * rounded rect.
+     */
+    public void setClipPath(Path clipPath) {
+        mClipPath = clipPath;
+        invalidate();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mClipPath != null) {
+            int count = canvas.save();
+            canvas.clipPath(mClipPath);
+            super.draw(canvas);
+            canvas.restoreToCount(count);
+        } else {
+            super.draw(canvas);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 1277a20..fa890b9 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+import static com.android.launcher3.folder.FolderShape.getShape;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -40,7 +41,6 @@
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Themes;
 
@@ -166,7 +166,6 @@
                 Math.round((totalOffsetX + initialSize) / initialScale),
                 Math.round((paddingOffsetY + initialSize) / initialScale));
         Rect endRect = new Rect(0, 0, lp.width, lp.height);
-        float initialRadius = initialSize / initialScale / 2f;
         float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
 
         // Create the animators.
@@ -189,14 +188,8 @@
         play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
         play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
         play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
-        RoundedRectRevealOutlineProvider outlineProvider = new RoundedRectRevealOutlineProvider(
-                initialRadius, finalRadius, startRect, endRect) {
-            @Override
-            public boolean shouldRemoveElevationDuringAnimation() {
-                return true;
-            }
-        };
-        play(a, outlineProvider.createRevealAnimator(mFolder, !mIsOpening));
+        play(a, getShape().createRevealAnimator(
+                mFolder, startRect, endRect, finalRadius, !mIsOpening));
 
         // Animate the elevation midway so that the shadow is not noticeable in the background.
         int midDuration = mDuration / 2;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index d09f036..429d44f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -75,6 +75,7 @@
  * An icon that can appear on in the workspace representing an {@link Folder}.
  */
 public class FolderIcon extends FrameLayout implements FolderListener {
+
     @Thunk Launcher mLauncher;
     @Thunk Folder mFolder;
     private FolderInfo mInfo;
@@ -477,20 +478,9 @@
         if (mFolder == null) return;
         if (mFolder.getItemCount() == 0 && !mAnimating) return;
 
-        final int saveCount;
-
-        if (canvas.isHardwareAccelerated()) {
-            saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
-        } else {
-            saveCount = canvas.save();
-            canvas.clipPath(mBackground.getClipPath());
-        }
-
+        final int saveCount = canvas.save();
+        canvas.clipPath(mBackground.getClipPath());
         mPreviewItemManager.draw(canvas);
-
-        if (canvas.isHardwareAccelerated()) {
-            mBackground.clipCanvasHardware(canvas);
-        }
         canvas.restoreToCount(saveCount);
 
         if (!mBackground.drawingDelegated()) {
diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/folder/FolderShape.java
new file mode 100644
index 0000000..ae279cb
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderShape.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2018 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.folder;
+
+import static com.android.launcher3.Workspace.MAP_NO_RECURSE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.FloatArrayEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.graphics.RegionIterator;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+
+/**
+ * Abstract representation of the shape of a folder icon
+ */
+public abstract class FolderShape {
+
+    private static FolderShape sInstance = new Circle();
+
+    public static FolderShape getShape() {
+        return sInstance;
+    }
+
+    private static FolderShape[] getAllShapes() {
+        return new FolderShape[] {
+                new Circle(),
+                new RoundedSquare(8f / 50),  // Ratios based on path defined in config_icon_mask
+                new RoundedSquare(30f / 50),
+                new Square(),
+                new TearDrop(),
+                new Squircle()};
+    }
+
+    public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
+            Paint paint);
+
+    public abstract void addShape(Path path, float offsetX, float offsetY, float radius);
+
+    public abstract Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
+            float endRadius, boolean isReversed);
+
+    /**
+     * Abstract shape where the reveal animation is a derivative of a round rect animation
+     */
+    private static abstract class SimpleRectShape extends FolderShape {
+
+        @Override
+        public final Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
+                float endRadius, boolean isReversed) {
+            return new RoundedRectRevealOutlineProvider(
+                    getStartRadius(startRect), endRadius, startRect, endRect) {
+                @Override
+                public boolean shouldRemoveElevationDuringAnimation() {
+                    return true;
+                }
+            }.createRevealAnimator(target, isReversed);
+        }
+
+        protected abstract float getStartRadius(Rect startRect);
+    }
+
+    /**
+     * Abstract shape which draws using {@link Path}
+     */
+    private static abstract class PathShape extends FolderShape {
+
+        private final Path mTmpPath = new Path();
+
+        @Override
+        public final void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
+                Paint paint) {
+            mTmpPath.reset();
+            addShape(mTmpPath, offsetX, offsetY, radius);
+            canvas.drawPath(mTmpPath, paint);
+        }
+
+        protected abstract AnimatorUpdateListener newUpdateListener(
+                Rect startRect, Rect endRect, float endRadius, Path outPath);
+
+        @Override
+        public final Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
+                float endRadius, boolean isReversed) {
+            Path path = new Path();
+            AnimatorUpdateListener listener =
+                    newUpdateListener(startRect, endRect, endRadius, path);
+
+            ValueAnimator va =
+                    isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
+            va.addListener(new AnimatorListenerAdapter() {
+                private ViewOutlineProvider mOldOutlineProvider;
+
+                public void onAnimationStart(Animator animation) {
+                    mOldOutlineProvider = target.getOutlineProvider();
+                    target.setOutlineProvider(null);
+
+                    target.setTranslationZ(-target.getElevation());
+                }
+
+                public void onAnimationEnd(Animator animation) {
+                    target.setTranslationZ(0);
+                    target.setClipPath(null);
+                    target.setOutlineProvider(mOldOutlineProvider);
+                }
+            });
+
+            va.addUpdateListener((anim) -> {
+                path.reset();
+                listener.onAnimationUpdate(anim);
+                target.setClipPath(path);
+            });
+
+            return va;
+        }
+    }
+
+    public static final class Circle extends SimpleRectShape {
+
+        @Override
+        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
+            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
+        }
+
+        @Override
+        public void addShape(Path path, float offsetX, float offsetY, float radius) {
+            path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
+        }
+
+        @Override
+        protected float getStartRadius(Rect startRect) {
+            return startRect.width() / 2f;
+        }
+    }
+
+    public static class Square extends SimpleRectShape {
+
+        @Override
+        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,  Paint p) {
+            float cx = radius + offsetX;
+            float cy = radius + offsetY;
+            canvas.drawRect(cx - radius, cy - radius, cx + radius, cy + radius, p);
+        }
+
+        @Override
+        public void addShape(Path path, float offsetX, float offsetY, float radius) {
+            float cx = radius + offsetX;
+            float cy = radius + offsetY;
+            path.addRect(cx - radius, cy - radius, cx + radius, cy + radius, Path.Direction.CW);
+        }
+
+        @Override
+        protected float getStartRadius(Rect startRect) {
+            return 0;
+        }
+    }
+
+    public static class RoundedSquare extends SimpleRectShape {
+
+        /**
+         * Ratio of corner radius to half size. Based on the
+         */
+        private final float mRadiusFactor;
+
+        public RoundedSquare(float radiusFactor) {
+            mRadiusFactor = radiusFactor;
+        }
+
+        @Override
+        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
+            float cx = radius + offsetX;
+            float cy = radius + offsetY;
+            float cr = radius * mRadiusFactor;
+            canvas.drawRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr, p);
+        }
+
+        @Override
+        public void addShape(Path path, float offsetX, float offsetY, float radius) {
+            float cx = radius + offsetX;
+            float cy = radius + offsetY;
+            float cr = radius * mRadiusFactor;
+            path.addRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr,
+                    Path.Direction.CW);
+        }
+
+        @Override
+        protected float getStartRadius(Rect startRect) {
+            return (startRect.width() / 2f) * mRadiusFactor;
+        }
+    }
+
+    public static class TearDrop extends PathShape {
+
+        /**
+         * Radio of short radius to large radius, based on the shape options defined in the config.
+         */
+        private static final float RADIUS_RATIO = 15f / 50;
+
+        private final float[] mTempRadii = new float[8];
+
+        @Override
+        public void addShape(Path p, float offsetX, float offsetY, float r1) {
+            float r2 = r1 * RADIUS_RATIO;
+            float cx = r1 + offsetX;
+            float cy = r1 + offsetY;
+
+            p.addRoundRect(cx - r1, cy - r1, cx + r1, cy + r1, getRadiiArray(r1, r2),
+                    Path.Direction.CW);
+        }
+
+        private float[] getRadiiArray(float r1, float r2) {
+            mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
+                    mTempRadii[6] = mTempRadii[7] = r1;
+            mTempRadii[4] = mTempRadii[5] = r2;
+            return mTempRadii;
+        }
+
+        @Override
+        protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+                float endRadius, Path outPath) {
+            float r1 = startRect.width() / 2f;
+            float r2 = r1 * RADIUS_RATIO;
+
+            float[] startValues = new float[] {
+                    startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r2};
+            float[] endValues = new float[] {
+                    endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
+
+            FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
+
+            return (anim) -> {
+                float progress = (Float) anim.getAnimatedValue();
+                float[] values = evaluator.evaluate(progress, startValues, endValues);
+                outPath.addRoundRect(
+                        values[0], values[1], values[2], values[3],
+                        getRadiiArray(values[4], values[5]), Path.Direction.CW);
+            };
+        }
+    }
+
+    public static class Squircle extends PathShape {
+
+        /**
+         * Radio of radius to circle radius, based on the shape options defined in the config.
+         */
+        private static final float RADIUS_RATIO = 10f / 50;
+
+        @Override
+        public void addShape(Path p, float offsetX, float offsetY, float r) {
+            float cx = r + offsetX;
+            float cy = r + offsetY;
+            float control = r - r * RADIUS_RATIO;
+
+            p.moveTo(cx, cy - r);
+            addLeftCurve(cx, cy, r, control, p);
+            addRightCurve(cx, cy, r, control, p);
+            addLeftCurve(cx, cy, -r, -control, p);
+            addRightCurve(cx, cy, -r, -control, p);
+            p.close();
+        }
+
+        private void addLeftCurve(float cx, float cy, float r, float control, Path path) {
+            path.cubicTo(
+                    cx - control, cy - r,
+                    cx - r, cy - control,
+                    cx - r, cy);
+        }
+
+        private void addRightCurve(float cx, float cy, float r, float control, Path path) {
+            path.cubicTo(
+                    cx - r, cy + control,
+                    cx - control, cy + r,
+                    cx, cy + r);
+        }
+
+        @Override
+        protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+                float endR, Path outPath) {
+
+            float startCX = startRect.exactCenterX();
+            float startCY = startRect.exactCenterY();
+            float startR = startRect.width() / 2f;
+            float startControl = startR - startR * RADIUS_RATIO;
+            float startHShift = 0;
+            float startVShift = 0;
+
+            float endCX = endRect.exactCenterX();
+            float endCY = endRect.exactCenterY();
+            // Approximate corner circle using bezier curves
+            // http://spencermortensen.com/articles/bezier-circle/
+            float endControl = endR * 0.551915024494f;
+            float endHShift = endRect.width() / 2f - endR;
+            float endVShift = endRect.height() / 2f - endR;
+
+            return (anim) -> {
+                float progress = (Float) anim.getAnimatedValue();
+
+                float cx = (1 - progress) * startCX + progress * endCX;
+                float cy = (1 - progress) * startCY + progress * endCY;
+                float r = (1 - progress) * startR + progress * endR;
+                float control = (1 - progress) * startControl + progress * endControl;
+                float hShift = (1 - progress) * startHShift + progress * endHShift;
+                float vShift = (1 - progress) * startVShift + progress * endVShift;
+
+                outPath.moveTo(cx, cy - vShift - r);
+                outPath.rLineTo(-hShift, 0);
+
+                addLeftCurve(cx - hShift, cy - vShift, r, control, outPath);
+                outPath.rLineTo(0, vShift + vShift);
+
+                addRightCurve(cx - hShift, cy + vShift, r, control, outPath);
+                outPath.rLineTo(hShift + hShift, 0);
+
+                addLeftCurve(cx + hShift, cy + vShift, -r, -control, outPath);
+                outPath.rLineTo(0, -vShift - vShift);
+
+                addRightCurve(cx + hShift, cy - vShift, -r, -control, outPath);
+                outPath.close();
+            };
+        }
+    }
+
+    /**
+     * Initializes the shape which is closest to closest to the {@link AdaptiveIconDrawable}
+     */
+    public static void init() {
+        if (!Utilities.ATLEAST_OREO) {
+            return;
+        }
+        new MainThreadExecutor().execute(FolderShape::pickShapeInBackground);
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    protected static void pickShapeInBackground() {
+        // Pick any large size
+        int size = 200;
+
+        Region full = new Region(0, 0, size, size);
+        Region iconR = new Region();
+        AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
+                new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
+        drawable.setBounds(0, 0, size, size);
+        iconR.setPath(drawable.getIconMask(), full);
+
+        Path shapePath = new Path();
+        Region shapeR = new Region();
+        Rect tempRect = new Rect();
+
+        // Find the shape with minimum area of divergent region.
+        int minArea = Integer.MAX_VALUE;
+        FolderShape closestShape = null;
+        for (FolderShape shape : getAllShapes()) {
+            shapePath.reset();
+            shape.addShape(shapePath, 0, 0, size / 2f);
+            shapeR.setPath(shapePath, full);
+            shapeR.op(iconR, Op.XOR);
+
+            RegionIterator itr = new RegionIterator(shapeR);
+            int area = 0;
+
+            while (itr.next(tempRect)) {
+                area += tempRect.width() * tempRect.height();
+            }
+            if (area < minArea) {
+                minArea = area;
+                closestShape = shape;
+            }
+        }
+
+        if (closestShape != null) {
+            FolderShape shape = closestShape;
+            new MainThreadExecutor().execute(() -> updateFolderShape(shape));
+        }
+    }
+
+    private static void updateFolderShape(FolderShape shape) {
+        sInstance = shape;
+        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+        if (app == null) {
+            return;
+        }
+        Launcher launcher = (Launcher) app.getModel().getCallback();
+        if (launcher != null) {
+            launcher.getWorkspace().mapOverItems(MAP_NO_RECURSE, (i, v) -> {
+                if (v instanceof FolderIcon) {
+                    v.invalidate();
+                }
+                return false;
+            });
+        }
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index ceb1a8c..8443953 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.folder.FolderShape.getShape;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -49,16 +51,6 @@
 
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
 
-    private final PorterDuffXfermode mClipPorterDuffXfermode
-            = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
-    // Create a RadialGradient such that it draws a black circle and then extends with
-    // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
-    // just at the edge quickly change it to transparent.
-    private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
-            new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
-            new float[] {0, 0.999f, 1},
-            Shader.TileMode.CLAMP);
-
     private final PorterDuffXfermode mShadowPorterDuffXfermode
             = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
     private RadialGradient mShadowShader = null;
@@ -208,8 +200,7 @@
         mPaint.setStyle(Paint.Style.FILL);
         mPaint.setColor(getBgColor());
 
-        drawCircle(canvas, 0 /* deltaRadius */);
-
+        getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
         drawShadow(canvas);
     }
 
@@ -244,7 +235,7 @@
         mPaint.setShader(null);
         if (canvas.isHardwareAccelerated()) {
             mPaint.setXfermode(mShadowPorterDuffXfermode);
-            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
+            getShape().drawShape(canvas, offsetX, offsetY, radius, mPaint);
             mPaint.setXfermode(null);
         }
 
@@ -287,7 +278,10 @@
         mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
         mPaint.setStyle(Paint.Style.STROKE);
         mPaint.setStrokeWidth(mStrokeWidth);
-        drawCircle(canvas, 1 /* deltaRadius */);
+
+        float inset = 1f;
+        getShape().drawShape(canvas,
+                getOffsetX() + inset, getOffsetY() + inset, getScaledRadius() - inset, mPaint);
     }
 
     public void drawLeaveBehind(Canvas canvas) {
@@ -296,40 +290,17 @@
 
         mPaint.setStyle(Paint.Style.FILL);
         mPaint.setColor(Color.argb(160, 245, 245, 245));
-        drawCircle(canvas, 0 /* deltaRadius */);
+        getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
 
         mScale = originalScale;
     }
 
-    private void drawCircle(Canvas canvas,float deltaRadius) {
-        float radius = getScaledRadius();
-        canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
-                radius - deltaRadius, mPaint);
-    }
-
     public Path getClipPath() {
         mPath.reset();
-        float r = getScaledRadius();
-        mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
+        getShape().addShape(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
         return mPath;
     }
 
-    // It is the callers responsibility to save and restore the canvas layers.
-    void clipCanvasHardware(Canvas canvas) {
-        mPaint.setColor(Color.BLACK);
-        mPaint.setStyle(Paint.Style.FILL);
-        mPaint.setXfermode(mClipPorterDuffXfermode);
-
-        float radius = getScaledRadius();
-        mShaderMatrix.setScale(radius, radius);
-        mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
-        mClipShader.setLocalMatrix(mShaderMatrix);
-        mPaint.setShader(mClipShader);
-        canvas.drawPaint(mPaint);
-        mPaint.setXfermode(null);
-        mPaint.setShader(null);
-    }
-
     private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
         if (mDrawingDelegate != delegate) {
             delegate.addFolderBackground(this);