More grid changes, closer to final specs.

* Added isScalable, minCellWidth, minCellHeight, and borderSpacing attrs

When isScalable is true, we use set workspace cell values to
minCellWidth/minCellHeight and then scale the cell values to
fit the available space. This allows us to have consistent
aspect ratios when two devices match the display/grid options.

This is different from the dynamic grid, which calculates
cell values based on iconSize/textSize/etc and then allows
the cell width to be as wide as space allows.

I adjusted some variables so that they will auto adjust
based on another value, indepenent from whether the
grid isScalable or not. An example of this is the folder
label text, where it's always set to be 1.14x of whatever
the workspace icon text size is. This is so we don't need
to add a bunch of more variables to DisplayOption/GridOption.

Bug: 175329686
Test: - switching between device profiles,
      - testing folder open/close
      - testing in multiwindow
      - testing in landscape
Change-Id: Ia469ae0d65b518469ef264b726db46f4a3210056
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 2a08c50..bb60557 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -111,6 +111,10 @@
     public float allAppsIconSize;
     public float allAppsIconTextSize;
 
+    public float minCellHeight;
+    public float minCellWidth;
+    public float borderSpacing;
+
     private SparseArray<TypedValue> mExtraAttrs;
 
     /**
@@ -123,6 +127,11 @@
      */
     public int numAllAppsColumns;
 
+    /**
+     * Do not query directly. see {@link DeviceProfile#isScalableGrid}.
+     */
+    protected boolean isScalable;
+
     public String dbFile;
     public int defaultLayoutId;
     int demoModeLayoutId;
@@ -154,6 +163,10 @@
         iconTextSize = p.iconTextSize;
         numHotseatIcons = p.numHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
+        isScalable = p.isScalable;
+        minCellHeight = p.minCellHeight;
+        minCellWidth = p.minCellWidth;
+        borderSpacing = p.borderSpacing;
         dbFile = p.dbFile;
         allAppsIconSize = p.allAppsIconSize;
         allAppsIconTextSize = p.allAppsIconTextSize;
@@ -213,6 +226,9 @@
         result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
         result.allAppsIconSize = Math.min(
                 defaultDisplayOption.allAppsIconSize, myDisplayOption.allAppsIconSize);
+        result.minCellHeight = defaultDisplayOption.minCellHeight;
+        result.minCellWidth = defaultDisplayOption.minCellWidth;
+        result.borderSpacing = defaultDisplayOption.borderSpacing;
 
         devicePaddings = new DevicePaddings(context);
         initGrid(context, myInfo, result);
@@ -259,6 +275,7 @@
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
         numAllAppsColumns = closestProfile.numAllAppsColumns;
+        isScalable = closestProfile.isScalable;
 
         mExtraAttrs = closestProfile.extraAttrs;
 
@@ -269,6 +286,10 @@
         iconTextSize = displayOption.iconTextSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
+        minCellHeight = displayOption.minCellHeight;
+        minCellWidth = displayOption.minCellWidth;
+        borderSpacing = displayOption.borderSpacing;
+
         if (Utilities.isGridOptionsEnabled(context)) {
             allAppsIconSize = displayOption.allAppsIconSize;
             allAppsIconTextSize = displayOption.allAppsIconTextSize;
@@ -589,6 +610,8 @@
         private final int defaultLayoutId;
         private final int demoModeLayoutId;
 
+        private final boolean isScalable;
+
         private final SparseArray<TypedValue> extraAttrs;
 
         public GridOption(Context context, AttributeSet attrs) {
@@ -612,6 +635,9 @@
             numAllAppsColumns = a.getInt(
                     R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
 
+            isScalable = a.getBoolean(
+                    R.styleable.GridDisplayOption_isScalable, false);
+
             a.recycle();
 
             extraAttrs = Themes.createValueMap(context, attrs,
@@ -626,6 +652,10 @@
         private final float minHeightDps;
         private final boolean canBeDefault;
 
+        private float minCellHeight;
+        private float minCellWidth;
+        private float borderSpacing;
+
         private float iconSize;
         private float iconTextSize;
         private float landscapeIconSize;
@@ -643,6 +673,10 @@
             canBeDefault = a.getBoolean(
                     R.styleable.ProfileDisplayOption_canBeDefault, false);
 
+            minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+            minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
+            borderSpacing = a.getFloat(R.styleable.ProfileDisplayOption_borderSpacingDps, 0);
+
             iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
             landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
                     iconSize);
@@ -664,6 +698,9 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
+            minCellHeight = 0;
+            minCellWidth = 0;
+            borderSpacing = 0;
         }
 
         private DisplayOption multiply(float w) {
@@ -672,6 +709,9 @@
             allAppsIconSize *= w;
             iconTextSize *= w;
             allAppsIconTextSize *= w;
+            minCellHeight *= w;
+            minCellWidth *= w;
+            borderSpacing *= w;
             return this;
         }
 
@@ -681,6 +721,9 @@
             allAppsIconSize += p.allAppsIconSize;
             iconTextSize += p.iconTextSize;
             allAppsIconTextSize += p.allAppsIconTextSize;
+            minCellHeight += p.minCellHeight;
+            minCellWidth += p.minCellWidth;
+            borderSpacing += p.borderSpacing;
             return this;
         }
     }