Folder icon polish.

- Increase the overlap between the preview items and the background
  so that it appears more intentional (lots of reports of users
  thinking that its a bug)
- Fix bug where clip path was still clipping icons when
  dragging and swiping to home.

Bug: 193701705
Bug: 191225536
Test: test 2 icon, 3 icon, and 4 icon folders
      close folder on 2nd page, ensure clipping
      ensure no preview items are clipped in drag view
      ensure no preview items are clipped in swipe up to home
      test 2x2, 3x3, 4x4, 4x5, and 5x5 grids to ensure no clipping
      of folder icons
      drag and drop folder in and out of hotseat
      create folders (2 icon, 3 icon, 4 icon)

Merged-In: Id49bae33438e10248bef05450d7e2c92c2140748
Change-Id: Ic3708bd402704f0a6f57ae5315ef602e2bbc6d82
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 88e8171..5a3e7dd 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.ResourceUtils.pxFromDp;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.pxFromSp;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
@@ -394,7 +395,8 @@
     }
 
     private void updateHotseatIconSize(int hotseatIconSizePx) {
-        hotseatCellHeightPx = hotseatIconSizePx;
+        // Ensure there is enough space for folder icons, which have a slightly larger radius.
+        hotseatCellHeightPx = (int) Math.ceil(hotseatIconSizePx * ICON_OVERLAP_FACTOR);
         if (isVerticalBarLayout()) {
             hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
                     + hotseatBarSidePaddingEndPx;
@@ -473,7 +475,7 @@
         if (workspaceCellPaddingY < iconTextHeight) {
             iconTextSizePx = 0;
             iconDrawablePaddingPx = 0;
-            cellHeightPx = iconSizePx;
+            cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR);
             autoResizeAllAppsCells();
         }
     }
@@ -560,7 +562,8 @@
             desiredWorkspaceLeftRightMarginPx = (int) (desiredWorkspaceLeftRightOriginalPx * scale);
         } else {
             cellWidthPx = iconSizePx + iconDrawablePaddingPx;
-            cellHeightPx = iconSizePx + iconDrawablePaddingPx
+            cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR)
+                    + iconDrawablePaddingPx
                     + Utilities.calculateTextHeight(iconTextSizePx);
             int cellPaddingY = (getCellSize().y - cellHeightPx) / 2;
             if (iconDrawablePaddingPx > cellPaddingY && !isVerticalLayout
@@ -844,14 +847,16 @@
         return isVerticalBarLayout();
     }
 
-    public int getCellHeight(@ContainerType int containerType) {
+    public int getCellContentHeight(@ContainerType int containerType) {
         switch (containerType) {
             case CellLayout.WORKSPACE:
                 return cellHeightPx;
             case CellLayout.FOLDER:
                 return folderCellHeightPx;
             case CellLayout.HOTSEAT:
-                return hotseatCellHeightPx;
+                // The hotseat is the only container where the cell height is going to be
+                // different from the content within that cell.
+                return iconSizePx;
             default:
                 // ??
                 return 0;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 42b4e89..0a6f06c 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -207,8 +207,8 @@
                 ? dp.workspacePadding.bottom
                 : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
 
-        if (dp.isScalableGrid && dp.qsbBottomMarginPx <= freeSpace) {
-            return dp.qsbBottomMarginPx;
+        if (dp.isScalableGrid) {
+            return Math.min(dp.qsbBottomMarginPx, freeSpace);
         } else {
             return (int) (freeSpace * QSB_CENTER_FACTOR) + (dp.isTaskbarPresent
                     ? dp.taskbarSize
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 2a5187d..519b63d 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -122,7 +122,7 @@
 
     public int getCellContentHeight() {
         return Math.min(getMeasuredHeight(),
-                mActivity.getDeviceProfile().getCellHeight(mContainerType));
+                mActivity.getDeviceProfile().getCellContentHeight(mContainerType));
     }
 
     public void measureChild(View child) {
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index ea1fbdb..98c0cfc 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -21,11 +21,11 @@
 import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.Log;
@@ -129,10 +129,19 @@
                     canvas.restore();
                 });
 
+        Bitmap bgBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
+                (canvas) -> {
+                    Paint p = new Paint();
+                    p.setColor(bg.getBgColor());
+
+                    canvas.drawCircle(dragViewSize.x / 2f, dragViewSize.y / 2f, bg.getRadius(), p);
+                });
+
         ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0);
         ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0);
+        ShiftedBitmapDrawable background = new ShiftedBitmapDrawable(bgBitmap, 0, 0);
 
-        return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
+        return new FolderAdaptiveIcon(background, foreground, badge, mask);
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 54967a99..8cd91d3 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -7,7 +7,9 @@
 
     private static final float MIN_SCALE = 0.44f;
     private static final float MAX_SCALE = 0.51f;
-    private static final float MAX_RADIUS_DILATION = 0.1f;
+    private static final float MAX_RADIUS_DILATION = 0.25f;
+    // The max amount of overlap the preview items can go outside of the background bounds.
+    public static final float ICON_OVERLAP_FACTOR = 1 + (MAX_RADIUS_DILATION / 2f);
     private static final float ITEM_RADIUS_SCALE_FACTOR = 1.15f;
 
     public static final int EXIT_INDEX = -2;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index e704957..96030f9 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
@@ -236,6 +237,8 @@
     public void getPreviewBounds(Rect outBounds) {
         mPreviewItemManager.recomputePreviewDrawingParams();
         mBackground.getBounds(outBounds);
+        // The preview items go outside of the bounds of the background.
+        Utilities.scaleRectAboutCenter(outBounds, ICON_OVERLAP_FACTOR);
     }
 
     public float getBackgroundStrokeWidth() {
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 460521f..18d0b10 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.graphics.IconShape.getShape;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
@@ -186,7 +187,7 @@
         outBounds.set(left, top, right, bottom);
     }
 
-    int getRadius() {
+    public int getRadius() {
         return previewSize / 2;
     }
 
@@ -348,7 +349,12 @@
 
     public Path getClipPath() {
         mPath.reset();
-        getShape().addToPath(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
+        float radius = getScaledRadius() * ICON_OVERLAP_FACTOR;
+        // Find the difference in radius so that the clip path remains centered.
+        float radiusDifference = radius - getRadius();
+        float offsetX = basePreviewOffsetX - radiusDifference;
+        float offsetY = basePreviewOffsetY - radiusDifference;
+        getShape().addToPath(mPath, offsetX, offsetY, radius);
         return mPath;
     }