Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2759aa34 authored by Thales Lima's avatar Thales Lima
Browse files

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
parent 9fb2079f
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -217,4 +217,28 @@
    <!-- Whether the floating rotation button should be on the left/right in the device's natural
         orientation -->
    <bool name="floating_rotation_button_position_left">true</bool>

    <!--  Mapping of visual icon size to XML value http://b/235886078  -->
    <dimen name="iconSize48dp">52dp</dimen>
    <dimen name="iconSize50dp">55dp</dimen>
    <dimen name="iconSize52dp">57dp</dimen>
    <dimen name="iconSize54dp">59dp</dimen>
    <dimen name="iconSize56dp">61dp</dimen>
    <dimen name="iconSize58dp">63dp</dimen>
    <dimen name="iconSize60dp">66dp</dimen>
    <dimen name="iconSize66dp">72dp</dimen>
    <dimen name="iconSize72dp">79dp</dimen>

    <!--  Icon size steps in dp  -->
    <integer-array name="icon_size_steps">
        <item>@dimen/iconSize48dp</item>
        <item>@dimen/iconSize50dp</item>
        <item>@dimen/iconSize52dp</item>
        <item>@dimen/iconSize54dp</item>
        <item>@dimen/iconSize56dp</item>
        <item>@dimen/iconSize58dp</item>
        <item>@dimen/iconSize60dp</item>
        <item>@dimen/iconSize66dp</item>
        <item>@dimen/iconSize72dp</item>
    </integer-array>
</resources>
+37 −6
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import com.android.launcher3.responsive.CalculatedAllAppsSpec;
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 class DeviceProfile {
    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 @@ public class DeviceProfile {
        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 @@ public class DeviceProfile {
        // 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 @@ public class DeviceProfile {

            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);
+47 −0
Original line number Diff line number Diff line
/*
 * 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 })
    }
}
+67 −0
Original line number Diff line number Diff line
/*
 * 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 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.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class IconSizeStepsTest {
    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 = 160 }
        context = runningContext.createConfigurationContext(config)
        iconSizeSteps = IconSizeSteps(context!!.resources)
    }

    @Test
    fun minimumIconSize() {
        assertThat(iconSizeSteps.minimumIconSize()).isEqualTo(52)
    }

    @Test
    fun getNextLowerIconSize() {
        assertThat(iconSizeSteps.getNextLowerIconSize(66)).isEqualTo(63)

        // Assert that never goes below minimum
        assertThat(iconSizeSteps.getNextLowerIconSize(52)).isEqualTo(52)
        assertThat(iconSizeSteps.getNextLowerIconSize(30)).isEqualTo(52)
    }

    @Test
    fun getIconSmallerThan() {
        assertThat(iconSizeSteps.getIconSmallerThan(60)).isEqualTo(59)

        // Assert that never goes below minimum
        assertThat(iconSizeSteps.getIconSmallerThan(52)).isEqualTo(52)
        assertThat(iconSizeSteps.getIconSmallerThan(30)).isEqualTo(52)
    }
}