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/res/values/config.xml b/res/values/config.xml
index cd9c9de..4b15a6b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -242,6 +242,8 @@
<item>@dimen/iconSize72dp</item>
</integer-array>
+ <dimen name="minimum_icon_label_size">8sp</dimen>
+
<!-- Used for custom widgets -->
<array name="custom_widget_providers"/>
</resources>
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
+ }
}
diff --git a/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt b/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt
new file mode 100644
index 0000000..4770546
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt
@@ -0,0 +1,148 @@
+/*
+ * 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 android.content.Context
+import android.content.res.Configuration
+import android.util.DisplayMetrics
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CellContentDimensionsTest {
+ private var context: Context? = null
+ private val runningContext: Context = ApplicationProvider.getApplicationContext()
+ private lateinit var iconSizeSteps: IconSizeSteps
+
+ @Before
+ fun setup() {
+ // 160dp makes 1px = 1dp
+ val config =
+ Configuration(runningContext.resources.configuration).apply {
+ this.densityDpi = DisplayMetrics.DENSITY_DEFAULT
+ }
+ context = runningContext.createConfigurationContext(config)
+ iconSizeSteps = IconSizeSteps(context!!.resources)
+ }
+
+ @Test
+ fun dimensionsFitTheCell() {
+ val cellSize = Pair(80, 104)
+ val cellContentDimensions =
+ CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+ val contentHeight =
+ cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+ assertThat(contentHeight).isEqualTo(93)
+ cellContentDimensions.run {
+ assertThat(iconSizePx).isEqualTo(66)
+ assertThat(iconDrawablePaddingPx).isEqualTo(8)
+ assertThat(iconTextSizePx).isEqualTo(14)
+ }
+ }
+
+ @Test
+ fun decreasePadding() {
+ val cellSize = Pair(67, 87)
+ val cellContentDimensions =
+ CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+ val contentHeight =
+ cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+ assertThat(contentHeight).isEqualTo(87)
+ cellContentDimensions.run {
+ assertThat(iconSizePx).isEqualTo(66)
+ assertThat(iconDrawablePaddingPx).isEqualTo(2)
+ assertThat(iconTextSizePx).isEqualTo(14)
+ }
+ }
+
+ @Test
+ fun decreaseIcon() {
+ val cellSize = Pair(65, 84)
+ val cellContentDimensions =
+ CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+ val contentHeight =
+ cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+ assertThat(contentHeight).isEqualTo(82)
+ cellContentDimensions.run {
+ assertThat(iconSizePx).isEqualTo(63)
+ assertThat(iconDrawablePaddingPx).isEqualTo(0)
+ assertThat(iconTextSizePx).isEqualTo(14)
+ }
+ }
+
+ @Test
+ fun decreaseText() {
+ val cellSize = Pair(63, 81)
+ val cellContentDimensions =
+ CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+ val contentHeight =
+ cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+ assertThat(contentHeight).isEqualTo(81)
+ cellContentDimensions.run {
+ assertThat(iconSizePx).isEqualTo(63)
+ assertThat(iconDrawablePaddingPx).isEqualTo(0)
+ assertThat(iconTextSizePx).isEqualTo(13)
+ }
+ }
+
+ @Test
+ fun decreaseIconAndTextTwoSteps() {
+ val cellSize = Pair(60, 78)
+ val cellContentDimensions =
+ CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+ val contentHeight =
+ cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+ assertThat(contentHeight).isEqualTo(77)
+ cellContentDimensions.run {
+ assertThat(iconSizePx).isEqualTo(61)
+ assertThat(iconDrawablePaddingPx).isEqualTo(0)
+ assertThat(iconTextSizePx).isEqualTo(12)
+ }
+ }
+
+ @Test
+ fun decreaseIconAndTextToMinimum() {
+ val cellSize = Pair(52, 63)
+ val cellContentDimensions =
+ CellContentDimensions(iconSizePx = 66, iconDrawablePaddingPx = 8, iconTextSizePx = 14)
+
+ val contentHeight =
+ cellContentDimensions.resizeToFitCellHeight(cellSize.second, iconSizeSteps)
+
+ assertThat(contentHeight).isEqualTo(63)
+ cellContentDimensions.run {
+ assertThat(iconSizePx).isEqualTo(52)
+ assertThat(iconDrawablePaddingPx).isEqualTo(0)
+ assertThat(iconTextSizePx).isEqualTo(8)
+ }
+ }
+}