Decrease icon size by steps
When the icon can't fit the cell size, decrease it by steps defined by UX until it fits or reach a minimum size.
Fix: 283929701
Test: DeviceProfileAlternativeDisplaysDumpTest
Test: DeviceProfileResponsiveAlternativeDisplaysDumpTest
Test: IconSizeStepsTest
Flag: ENABLE_RESPONSIVE_WORKSPACE
Change-Id: I2875b669c0a24ecd1c4d785a33e2cffb78c9fe76
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 0498032..a166271 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -58,6 +58,7 @@
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.IconSizeSteps;
import com.android.launcher3.util.ResourceHelper;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.workspace.CalculatedWorkspaceSpec;
@@ -83,6 +84,7 @@
public final InvariantDeviceProfile inv;
private final Info mInfo;
private final DisplayMetrics mMetrics;
+ private final IconSizeSteps mIconSizeSteps;
// Device properties
public final boolean isTablet;
@@ -330,6 +332,8 @@
final Resources res = context.getResources();
mMetrics = res.getDisplayMetrics();
+ mIconSizeSteps = mIsResponsiveGrid ? new IconSizeSteps(res) : null;
+
// Determine sizes.
widthPx = windowBounds.bounds.width();
heightPx = windowBounds.bounds.height();
@@ -535,12 +539,16 @@
// for the available height to be correct
if (mIsResponsiveGrid) {
mWorkspaceSpecs = new WorkspaceSpecs(new ResourceHelper(context, inv.workspaceSpecsId));
+ int availableResponsiveWidth =
+ availableWidthPx - (isVerticalBarLayout() ? hotseatBarSizePx : 0);
+ // don't use availableHeightPx because it subtracts bottom padding,
+ // but the workspace go behind it
+ int availableResponsiveHeight =
+ heightPx - mInsets.top - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
mResponsiveWidthSpec = mWorkspaceSpecs.getCalculatedWidthSpec(inv.numColumns,
- availableWidthPx);
+ availableResponsiveWidth);
mResponsiveHeightSpec = mWorkspaceSpecs.getCalculatedHeightSpec(inv.numRows,
- // don't use availableHeightPx because it subtracts bottom padding,
- // but the hotseat go behind it
- heightPx - mInsets.top - hotseatBarSizePx);
+ availableResponsiveHeight);
mAllAppsSpecs = new AllAppsSpecs(new ResourceHelper(context, inv.allAppsSpecsId));
mAllAppsResponsiveWidthSpec = mAllAppsSpecs.getCalculatedWidthSpec(inv.numColumns,
@@ -926,9 +934,32 @@
cellWidthPx = mResponsiveWidthSpec.getCellSizePx();
cellHeightPx = mResponsiveHeightSpec.getCellSizePx();
- cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
- // TODO(b/283929701): decrease icon size if content doesn't fit on cell
+ if (cellWidthPx < iconSizePx) {
+ // get a smaller icon size
+ iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
+ // calculate new cellContentHeight
+ cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+ }
+
+ while (iconSizePx > mIconSizeSteps.minimumIconSize()
+ && cellContentHeight > cellHeightPx) {
+ int extraHeightRequired = cellContentHeight - cellHeightPx;
+ int newPadding = iconDrawablePaddingPx - extraHeightRequired;
+ if (newPadding >= 0) {
+ // Responsive uses the padding without scaling
+ iconDrawablePaddingPx = iconDrawablePaddingOriginalPx = newPadding;
+ cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
+ } else {
+ // get a smaller icon size
+ iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx);
+ }
+ // calculate new cellContentHeight
+ cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
+ }
+
+ cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
} else if (mIsScalableGrid) {
cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
diff --git a/src/com/android/launcher3/util/IconSizeSteps.kt b/src/com/android/launcher3/util/IconSizeSteps.kt
new file mode 100644
index 0000000..2a5afe0
--- /dev/null
+++ b/src/com/android/launcher3/util/IconSizeSteps.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.res.Resources
+import androidx.core.content.res.getDimensionOrThrow
+import androidx.core.content.res.use
+import com.android.launcher3.R
+import kotlin.math.max
+
+class IconSizeSteps(res: Resources) {
+ private val steps: List<Int>
+
+ init {
+ steps =
+ res.obtainTypedArray(R.array.icon_size_steps).use {
+ (0 until it.length()).map { step -> it.getDimensionOrThrow(step).toInt() }.sorted()
+ }
+ }
+
+ fun minimumIconSize(): Int = steps[0]
+
+ fun getNextLowerIconSize(iconSizePx: Int): Int {
+ return steps[max(0, getIndexForIconSize(iconSizePx) - 1)]
+ }
+
+ fun getIconSmallerThan(cellWidth: Int): Int {
+ return steps.lastOrNull { it <= cellWidth } ?: steps[0]
+ }
+
+ private fun getIndexForIconSize(iconSizePx: Int): Int {
+ return max(0, steps.indexOfFirst { iconSizePx <= it })
+ }
+}