Decrease text size if it doesn't fit the cell

Bug: 293467738
Test: ResponsiveHomeScreenImageTest
Test: ResponsiveAllAppsImageTest
Test: DeviceProfileResponsiveDumpTest
Flag: ENABLE_RESPONSIVE_WORKSPACE
Change-Id: Ic0b2d3cde1d4b8b9089f93b96c9bb386247603ce
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 2ac6098..e905062 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -63,6 +63,7 @@
 import com.android.launcher3.responsive.HotseatSpecs;
 import com.android.launcher3.responsive.WorkspaceSpecs;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.CellContentDimensions;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IconSizeSteps;
@@ -1049,20 +1050,20 @@
 
             // TODO(b/296400197): isVerticalBar shouldn't show labels anymore
             iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
-            int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
-            int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
 
-            while (iconSizePx > mIconSizeSteps.minimumIconSize()
-                    && cellContentHeight > cellHeightPx) {
-                iconDrawablePaddingPx -= cellContentHeight - cellHeightPx;
-                if (iconDrawablePaddingPx < 0) {
-                    // get a smaller icon size
-                    iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx);
-                    iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
-                }
-                // calculate new cellContentHeight
-                cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
+            CellContentDimensions cellContentDimensions = new CellContentDimensions(iconSizePx,
+                    iconDrawablePaddingPx,
+                    iconTextSizePx);
+            if (isVerticalLayout && cellHeightPx < iconSizePx) {
+                cellContentDimensions.setIconSizePx(
+                        mIconSizeSteps.getIconSmallerThan(cellHeightPx));
+            } else {
+                cellContentDimensions.resizeToFitCellHeight(cellHeightPx, mIconSizeSteps);
             }
+            iconSizePx = cellContentDimensions.getIconSizePx();
+            iconDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+            iconTextSizePx = cellContentDimensions.getIconTextSizePx();
+            int cellContentHeight = cellContentDimensions.getCellContentHeight();
 
             cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
         } else if (mIsScalableGrid) {
diff --git a/src/com/android/launcher3/util/CellContentDimensions.kt b/src/com/android/launcher3/util/CellContentDimensions.kt
new file mode 100644
index 0000000..3c8e0c4
--- /dev/null
+++ b/src/com/android/launcher3/util/CellContentDimensions.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 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.util
+
+import com.android.launcher3.Utilities
+import kotlin.math.max
+
+class CellContentDimensions(
+    var iconSizePx: Int,
+    var iconDrawablePaddingPx: Int,
+    var iconTextSizePx: Int
+) {
+    /**
+     * This method goes through some steps to reduce the padding between icon and label, icon size
+     * and then label size, until it can fit in the [cellHeightPx].
+     *
+     * @return the height of the content after being sized down.
+     */
+    fun resizeToFitCellHeight(cellHeightPx: Int, iconSizeSteps: IconSizeSteps): Int {
+        var cellContentHeight = getCellContentHeight()
+
+        // Step 1. Decrease drawable padding
+        if (cellContentHeight > cellHeightPx) {
+            val diff = cellContentHeight - cellHeightPx
+            iconDrawablePaddingPx = max(0, iconDrawablePaddingPx - diff)
+            cellContentHeight = getCellContentHeight()
+        }
+
+        while (
+            (iconTextSizePx > iconSizeSteps.minimumIconLabelSize ||
+                iconSizePx > iconSizeSteps.minimumIconSize()) && cellContentHeight > cellHeightPx
+        ) {
+            // Step 2. Decrease icon size
+            iconSizePx = iconSizeSteps.getNextLowerIconSize(iconSizePx)
+            cellContentHeight = getCellContentHeight()
+
+            // Step 3. Decrease label size
+            if (cellContentHeight > cellHeightPx) {
+                iconTextSizePx =
+                    max(
+                        iconSizeSteps.minimumIconLabelSize,
+                        iconTextSizePx - IconSizeSteps.TEXT_STEP
+                    )
+                cellContentHeight = getCellContentHeight()
+            }
+        }
+
+        return cellContentHeight
+    }
+
+    /** Calculate new cellContentHeight */
+    fun getCellContentHeight(): Int {
+        val iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx.toFloat())
+        return iconSizePx + iconDrawablePaddingPx + iconTextHeight
+    }
+}
diff --git a/src/com/android/launcher3/util/IconSizeSteps.kt b/src/com/android/launcher3/util/IconSizeSteps.kt
index 2a5afe0..aa644b0 100644
--- a/src/com/android/launcher3/util/IconSizeSteps.kt
+++ b/src/com/android/launcher3/util/IconSizeSteps.kt
@@ -23,12 +23,14 @@
 
 class IconSizeSteps(res: Resources) {
     private val steps: List<Int>
+    val minimumIconLabelSize: Int
 
     init {
         steps =
             res.obtainTypedArray(R.array.icon_size_steps).use {
                 (0 until it.length()).map { step -> it.getDimensionOrThrow(step).toInt() }.sorted()
             }
+        minimumIconLabelSize = res.getDimensionPixelSize(R.dimen.minimum_icon_label_size)
     }
 
     fun minimumIconSize(): Int = steps[0]
@@ -44,4 +46,8 @@
     private fun getIndexForIconSize(iconSizePx: Int): Int {
         return max(0, steps.indexOfFirst { iconSizePx <= it })
     }
+
+    companion object {
+        internal const val TEXT_STEP = 1
+    }
 }