From 24ceca6993aedc27bcfa379a38eec559016a9fc6 Mon Sep 17 00:00:00 2001 From: Thales Lima Date: Mon, 25 Sep 2023 16:21:17 -0700 Subject: [PATCH] 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 --- res/values/config.xml | 2 + src/com/android/launcher3/DeviceProfile.java | 25 +-- .../launcher3/util/CellContentDimensions.kt | 69 ++++++++ .../android/launcher3/util/IconSizeSteps.kt | 6 + .../util/CellContentDimensionsTest.kt | 148 ++++++++++++++++++ 5 files changed, 238 insertions(+), 12 deletions(-) create mode 100644 src/com/android/launcher3/util/CellContentDimensions.kt create mode 100644 tests/src/com/android/launcher3/util/CellContentDimensionsTest.kt diff --git a/res/values/config.xml b/res/values/config.xml index cd9c9de69f..4b15a6ba7f 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -242,6 +242,8 @@ @dimen/iconSize72dp + 8sp + diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 2ac60984c6..e905062211 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -63,6 +63,7 @@ import com.android.launcher3.responsive.FolderSpecs; 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 @@ public class DeviceProfile { // 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 0000000000..3c8e0c44ca --- /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 2a5afe0ce2..aa644b0d59 100644 --- a/src/com/android/launcher3/util/IconSizeSteps.kt +++ b/src/com/android/launcher3/util/IconSizeSteps.kt @@ -23,12 +23,14 @@ import kotlin.math.max class IconSizeSteps(res: Resources) { private val steps: List + 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 @@ class IconSizeSteps(res: Resources) { 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 0000000000..4770546e51 --- /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) + } + } +}