Calculate hotseat width based on available width
Navigation buttons take space on the hotseat, so hotseat width (qsb width and border space between icons) should be calculated instead of having a coded value.
Bug: 223724516
Bug: 228998463
Test: manual, changing display size
Test: manual, changing hotseatBarEndOffset value
Test: HotseatWidthCalculationTest
Change-Id: Ibd4f5ff2e06afda8e7420fb744db049d2e418e14
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 8368069..905fbda 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -77,7 +77,7 @@
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_margin_5_5">94.5dp</dimen>
- <dimen name="taskbar_button_margin_6_5">94.5dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">219.6dp</dimen>
<dimen name="taskbar_button_margin_4_5">84dp</dimen>
<dimen name="taskbar_button_margin_4_4">79dp</dimen>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index cfe602e..6bc3d38 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -278,7 +278,7 @@
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_space_inbetween">24dp</dimen>
<dimen name="taskbar_button_margin_5_5">26dp</dimen>
- <dimen name="taskbar_button_margin_6_5">26dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">75dp</dimen>
<dimen name="taskbar_button_margin_4_5">47dp</dimen>
<dimen name="taskbar_button_margin_4_4">47dp</dimen>
<dimen name="taskbar_button_margin_default">47dp</dimen>
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
index b50d3ee..45a342a 100644
--- a/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTest.kt
@@ -338,18 +338,18 @@
"\thotseatBarBottomSpacePx: 80.0px (40.0dp)\n" +
"\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
"\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 597.0px (298.5dp)\n" +
+ "\thotseatBarEndOffset: 705.0px (352.5dp)\n" +
"\thotseatQsbSpace: 64.0px (32.0dp)\n" +
"\thotseatQsbHeight: 126.0px (63.0dp)\n" +
"\tspringLoadedHotseatBarTopMarginPx: 128.0px (64.0dp)\n" +
"\tgetHotseatLayoutPadding(context).top: -8.0px (-4.0dp)\n" +
"\tgetHotseatLayoutPadding(context).bottom: 73.0px (36.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 959.0px (479.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 597.0px (298.5dp)\n" +
- "\tnumShownHotseatIcons: 5\n" +
- "\thotseatBorderSpace: 101.0px (50.5dp)\n" +
+ "\tgetHotseatLayoutPadding(context).left: 954.0px (477.0dp)\n" +
+ "\tgetHotseatLayoutPadding(context).right: 705.0px (352.5dp)\n" +
+ "\tnumShownHotseatIcons: 6\n" +
+ "\thotseatBorderSpace: 36.0px (18.0dp)\n" +
"\tisQsbInline: true\n" +
- "\thotseatQsbWidth: 855.0px (427.5dp)\n" +
+ "\thotseatQsbWidth: 619.0px (309.5dp)\n" +
"\tisTaskbarPresent:true\n" +
"\tisTaskbarPresentInApps:true\n" +
"\ttaskbarSize: 120.0px (60.0dp)\n" +
@@ -578,16 +578,16 @@
"\thotseatBarBottomSpacePx: 72.0px (36.0dp)\n" +
"\thotseatBarSidePaddingStartPx: 0.0px (0.0dp)\n" +
"\thotseatBarSidePaddingEndPx: 0.0px (0.0dp)\n" +
- "\thotseatBarEndOffset: 460.0px (230.0dp)\n" +
+ "\thotseatBarEndOffset: 558.0px (279.0dp)\n" +
"\thotseatQsbSpace: 64.0px (32.0dp)\n" +
"\thotseatQsbHeight: 126.0px (63.0dp)\n" +
"\tspringLoadedHotseatBarTopMarginPx: 216.0px (108.0dp)\n" +
"\tgetHotseatLayoutPadding(context).top: 158.0px (79.0dp)\n" +
"\tgetHotseatLayoutPadding(context).bottom: 65.0px (32.5dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 76.0px (38.0dp)\n" +
- "\tgetHotseatLayoutPadding(context).right: 460.0px (230.0dp)\n" +
+ "\tgetHotseatLayoutPadding(context).left: 150.0px (75.0dp)\n" +
+ "\tgetHotseatLayoutPadding(context).right: 558.0px (279.0dp)\n" +
"\tnumShownHotseatIcons: 5\n" +
- "\thotseatBorderSpace: 116.0px (58.0dp)\n" +
+ "\thotseatBorderSpace: 73.0px (36.5dp)\n" +
"\tisQsbInline: false\n" +
"\thotseatQsbWidth: 1300.0px (650.0dp)\n" +
"\tisTaskbarPresent:true\n" +
@@ -824,10 +824,10 @@
"\tspringLoadedHotseatBarTopMarginPx: 116.0px (44.190475dp)\n" +
"\tgetHotseatLayoutPadding(context).top: 197.0px (75.04762dp)\n" +
"\tgetHotseatLayoutPadding(context).bottom: 43.0px (16.380953dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 216.0px (82.28571dp)\n" +
+ "\tgetHotseatLayoutPadding(context).left: 106.0px (40.38095dp)\n" +
"\tgetHotseatLayoutPadding(context).right: 744.0px (283.42856dp)\n" +
"\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 61.0px (23.238094dp)\n" +
+ "\thotseatBorderSpace: 83.0px (31.619047dp)\n" +
"\tisQsbInline: false\n" +
"\thotseatQsbWidth: 1467.0px (558.8571dp)\n" +
"\tisTaskbarPresent:true\n" +
@@ -1064,10 +1064,10 @@
"\tspringLoadedHotseatBarTopMarginPx: 171.0px (65.14286dp)\n" +
"\tgetHotseatLayoutPadding(context).top: 219.0px (83.42857dp)\n" +
"\tgetHotseatLayoutPadding(context).bottom: 87.0px (33.142857dp)\n" +
- "\tgetHotseatLayoutPadding(context).left: 128.0px (48.761906dp)\n" +
+ "\tgetHotseatLayoutPadding(context).left: 78.0px (29.714285dp)\n" +
"\tgetHotseatLayoutPadding(context).right: 660.0px (251.42857dp)\n" +
"\tnumShownHotseatIcons: 6\n" +
- "\thotseatBorderSpace: 47.0px (17.904762dp)\n" +
+ "\thotseatBorderSpace: 57.0px (21.714285dp)\n" +
"\tisQsbInline: false\n" +
"\thotseatQsbWidth: 1236.0px (470.85715dp)\n" +
"\tisTaskbarPresent:true\n" +
diff --git a/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
new file mode 100644
index 0000000..3967f75
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 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.quickstep
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.launcher3.util.WindowBounds
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HotseatWidthCalculationTest : DeviceProfileBaseTest() {
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated
+ * but it doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1800, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(558)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(69)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(176)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(558)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1445)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, and recalculating spaces
+ * it still needs to remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1300, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(558)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(76)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(122)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(558)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1058)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated
+ * but it doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_landscape() {
+ initializeVarsForTwoPanel(isGestureMode = false, isLandscape = true)
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(744)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(83)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(106)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(744)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1467)
+ }
+
+ /**
+ * This is a case when the hotseat spans a certain amount of columns
+ * and the nav buttons push the hotseat to the side, but not enough to change the border space.
+ */
+ @Test
+ fun nav_buttons_dont_interfere_with_required_hotseat_width() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ inv?.apply {
+ hotseatColumnSpan = IntArray(4) { 4 }
+ inlineQsb = BooleanArray(4) { false }
+ }
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(705)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(108)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(631)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(705)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1227)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, the QSB width needs to be changed to fit
+ */
+ @Test
+ fun decrease_qsb_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2460, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(705)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(36)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(884)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(705)
+
+ assertThat(dp.isQsbInline).isTrue()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(559)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, changing QSB width, and recalculating spaces
+ * it still needs to remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2260, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(705)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(56)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(801)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(705)
+
+ assertThat(dp.isQsbInline).isTrue()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(480)
+ }
+}
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 3b71585..28d094b 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -158,9 +158,6 @@
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
- <!-- Number of icons to use when shrinking the hotseat size,
- defaults to numHotseatIcons / 2 -->
- <attr name="numShrunkenHotseatIcons" format="integer" />
<!-- Number of icons to use when extending the hotseat size,
defaults to 2 * numHotseatIcons -->
<attr name="numExtendedHotseatIcons" format="integer" />
@@ -328,15 +325,6 @@
if not specified -->
<attr name="allAppsBorderSpaceTwoPanelLandscapeVertical" format="float" />
- <!-- defaults to borderSpaceDps, if not specified -->
- <attr name="hotseatBorderSpace" format="float" />
- <!-- defaults to hotseatBorderSpace, if not specified -->
- <attr name="hotseatBorderSpaceLandscape" format="float" />
- <!-- defaults to hotseatBorderSpace, if not specified -->
- <attr name="hotseatBorderSpaceTwoPanelLandscape" format="float" />
- <!-- defaults to hotseatBorderSpace, if not specified -->
- <attr name="hotseatBorderSpaceTwoPanelPortrait" format="float" />
-
<!-- defaults to res.hotseat_bar_bottom_space_default, if not specified -->
<attr name="hotseatBarBottomSpace" format="float" />
<!-- defaults to hotseatBarBottomSpace, if not specified -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c8554ec..546ee35 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -354,6 +354,8 @@
<dimen name="taskbar_stashed_size">0dp</dimen>
<dimen name="qsb_widget_height">0dp</dimen>
<dimen name="qsb_shadow_height">0dp</dimen>
+ <dimen name="min_hotseat_icon_space">18dp</dimen>
+ <dimen name="min_hotseat_qsb_width">0dp</dimen>
<dimen name="taskbar_icon_size">44dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 0238e7d..407f217 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -200,8 +200,6 @@
launcher:allAppsBorderSpaceHorizontal="8"
launcher:allAppsBorderSpaceVertical="16"
launcher:allAppsBorderSpaceLandscape="16"
- launcher:hotseatBorderSpace="58"
- launcher:hotseatBorderSpaceLandscape="50.4"
launcher:hotseatBarBottomSpace="76"
launcher:hotseatBarBottomSpaceLandscape="40"
launcher:canBeDefault="true" />
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 0d41230..f27eb79 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -158,7 +158,7 @@
public int folderChildDrawablePaddingPx;
// Hotseat
- public final int numShownHotseatIcons;
+ public int numShownHotseatIcons;
public int hotseatCellHeightPx;
public final boolean areNavButtonsInline;
// In portrait: size = height, in landscape: size = width
@@ -362,15 +362,9 @@
&& hotseatQsbHeight > 0;
isQsbInline = inv.inlineQsb[mTypeIndex] && canQsbInline;
- // We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB
- // might be inline in either orientations, to keep hotseat size consistent across rotation.
areNavButtonsInline = isTaskbarPresent && !isGestureMode;
- if (areNavButtonsInline && canQsbInline) {
- numShownHotseatIcons = inv.numShrunkenHotseatIcons;
- } else {
- numShownHotseatIcons =
- isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
- }
+ numShownHotseatIcons =
+ isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
numShownAllAppsColumns =
isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
@@ -460,8 +454,7 @@
updateWorkspacePadding();
// Hotseat and QSB width depends on updated cellSize and workspace padding
- hotseatBorderSpace = calculateHotseatBorderSpace();
- hotseatQsbWidth = calculateQsbWidth();
+ recalculateHotseatWidthAndBorderSpace(res);
// AllApps height calculation depends on updated cellSize
if (isTablet) {
@@ -499,7 +492,7 @@
* QSB width is always calculated because when in 3 button nav the width doesn't follow the
* width of the hotseat.
*/
- private int calculateQsbWidth() {
+ private int calculateQsbWidth(int hotseatBorderSpace) {
if (isQsbInline) {
int columns = getPanelCount() * inv.numColumns;
return getIconToIconWidthForColumns(columns)
@@ -546,6 +539,70 @@
}
}
+ private void recalculateHotseatWidthAndBorderSpace(Resources res) {
+ hotseatBorderSpace = calculateHotseatBorderSpace();
+ hotseatQsbWidth = calculateQsbWidth(hotseatBorderSpace);
+ // Spaces should be correct when there nav buttons are not inline
+ if (!areNavButtonsInline) {
+ return;
+ }
+
+ // Get the maximum width that the hotseat can be
+ int columns = getPanelCount() * inv.numColumns;
+ int maxHotseatWidth = getIconToIconWidthForColumns(columns);
+ int sideSpace = (availableWidthPx - maxHotseatWidth) / 2;
+ int inlineButtonsOverlap = Math.max(0, hotseatBarEndOffset - sideSpace);
+ // decrease how much the nav buttons go "inside" the hotseat
+ maxHotseatWidth -= inlineButtonsOverlap;
+
+ // Get how much space is required to show the hotseat with QSB
+ int requiredWidth = getHotseatRequiredWidth();
+
+ // If spaces are fine, use them
+ if (requiredWidth <= maxHotseatWidth) {
+ return;
+ }
+
+ // Calculate the difference of widths and remove a little from each space between icons
+ // and QSB if it's inline
+ int spaceDiff = requiredWidth - maxHotseatWidth;
+ int numOfSpaces = numShownHotseatIcons - (isQsbInline ? 0 : 1);
+ hotseatBorderSpace -= (spaceDiff / numOfSpaces);
+
+ int minHotseatIconSpaceDp = res.getDimensionPixelSize(R.dimen.min_hotseat_icon_space);
+ int minHotseatQsbWidthDp = res.getDimensionPixelSize(R.dimen.min_hotseat_qsb_width);
+
+ if (hotseatBorderSpace >= minHotseatIconSpaceDp) {
+ return;
+ }
+
+ // Border space can't be less than the minimum
+ hotseatBorderSpace = minHotseatIconSpaceDp;
+ requiredWidth = getHotseatRequiredWidth();
+
+ // If there is an inline qsb, change its size
+ if (isQsbInline) {
+ hotseatQsbWidth -= requiredWidth - maxHotseatWidth;
+ if (hotseatQsbWidth >= minHotseatQsbWidthDp) {
+ return;
+ }
+
+ // QSB can't be less than the minimum
+ hotseatQsbWidth = minHotseatQsbWidthDp;
+ }
+
+ // If it still doesn't fit, start removing icons
+ do {
+ numShownHotseatIcons--;
+ requiredWidth = getHotseatRequiredWidth();
+ } while (requiredWidth > maxHotseatWidth && numShownHotseatIcons > 1);
+
+ // Add back some space between the icons
+ spaceDiff = maxHotseatWidth - requiredWidth;
+ numOfSpaces = numShownHotseatIcons - (isQsbInline ? 0 : 1);
+ hotseatBorderSpace += (spaceDiff / numOfSpaces);
+ }
+
private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp) {
return getCellLayoutBorderSpace(idp, 1f);
}
@@ -778,15 +835,10 @@
*/
private int calculateHotseatBorderSpace() {
if (!isScalableGrid) return 0;
- //TODO(http://b/228998082) remove this when 3 button spaces are fixed
- if (areNavButtonsInline) {
- return pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
- } else {
- int columns = inv.hotseatColumnSpan[mTypeIndex];
- float hotseatWidthPx = getIconToIconWidthForColumns(columns);
- float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
- return (int) (hotseatWidthPx - hotseatIconsTotalPx) / (numShownHotseatIcons - 1);
- }
+ int columns = inv.hotseatColumnSpan[mTypeIndex];
+ float hotseatWidthPx = getIconToIconWidthForColumns(columns);
+ float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
+ return (int) (hotseatWidthPx - hotseatIconsTotalPx) / (numShownHotseatIcons - 1);
}
@@ -1078,10 +1130,7 @@
hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx;
// Push icons to the side
- int additionalQsbSpace = isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
- int requiredWidth = iconSizePx * numShownHotseatIcons
- + hotseatBorderSpace * (numShownHotseatIcons - 1)
- + additionalQsbSpace;
+ int requiredWidth = getHotseatRequiredWidth();
int hotseatWidth = Math.min(requiredWidth, availableWidthPx - hotseatBarEndOffset);
int sideSpacing = (availableWidthPx - hotseatWidth) / 2;
@@ -1090,9 +1139,9 @@
boolean isRtl = Utilities.isRtl(context.getResources());
if (isRtl) {
- hotseatBarPadding.right += additionalQsbSpace;
+ hotseatBarPadding.right += getAdditionalQsbSpace();
} else {
- hotseatBarPadding.left += additionalQsbSpace;
+ hotseatBarPadding.left += getAdditionalQsbSpace();
}
if (hotseatBarEndOffset > sideSpacing) {
@@ -1127,6 +1176,20 @@
return hotseatBarPadding;
}
+ private int getAdditionalQsbSpace() {
+ return isQsbInline ? hotseatQsbWidth + hotseatBorderSpace : 0;
+ }
+
+ /**
+ * Calculate how much space the hotseat needs to be shown completely
+ */
+ private int getHotseatRequiredWidth() {
+ int additionalQsbSpace = getAdditionalQsbSpace();
+ return iconSizePx * numShownHotseatIcons
+ + hotseatBorderSpace * (numShownHotseatIcons - 1)
+ + additionalQsbSpace;
+ }
+
/**
* Returns the number of pixels the QSB is translated from the bottom of the screen.
*/
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 67620e3..21eba00 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -128,7 +128,6 @@
public PointF[] borderSpaces;
public float folderBorderSpace;
- public float[] hotseatBorderSpaces;
public int inlineNavButtonsEndSpacing;
public float[] horizontalMargin;
@@ -146,11 +145,6 @@
public int numShownHotseatIcons;
/**
- * Number of icons inside the hotseat area when using 3 buttons navigation.
- */
- public int numShrunkenHotseatIcons;
-
- /**
* Number of icons inside the hotseat area that is stored in the database. This is greater than
* or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat
* sizes that share the same DB.
@@ -176,7 +170,7 @@
public String dbFile;
public int defaultLayoutId;
int demoModeLayoutId;
- boolean[] inlineQsb = new boolean[COUNT_SIZES];
+ public boolean[] inlineQsb = new boolean[COUNT_SIZES];
/**
* An immutable list of supported profiles.
@@ -364,11 +358,9 @@
horizontalMargin = displayOption.horizontalMargin;
numShownHotseatIcons = closestProfile.numHotseatIcons;
- numShrunkenHotseatIcons = closestProfile.numShrunkenHotseatIcons;
numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
hotseatColumnSpan = closestProfile.hotseatColumnSpan;
- hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
hotseatBarBottomSpace = displayOption.hotseatBarBottomSpace;
hotseatQsbSpace = displayOption.hotseatQsbSpace;
@@ -737,7 +729,6 @@
private final int numAllAppsColumns;
private final int numDatabaseAllAppsColumns;
private final int numHotseatIcons;
- private final int numShrunkenHotseatIcons;
private final int numDatabaseHotseatIcons;
private final int[] hotseatColumnSpan = new int[COUNT_SIZES];
@@ -777,8 +768,6 @@
numHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
- numShrunkenHotseatIcons = a.getInt(
- R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2);
numDatabaseHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
@@ -842,8 +831,6 @@
private float folderBorderSpace;
private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
private final float[] horizontalMargin = new float[COUNT_SIZES];
- //TODO(http://b/228998082) remove this when 3 button spaces are fixed
- private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES];
private final float[] hotseatQsbSpace = new float[COUNT_SIZES];
@@ -1059,18 +1046,6 @@
R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelPortrait,
horizontalMargin[INDEX_DEFAULT]);
- hotseatBorderSpaces[INDEX_DEFAULT] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpace, borderSpace);
- hotseatBorderSpaces[INDEX_LANDSCAPE] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpaceLandscape,
- hotseatBorderSpaces[INDEX_DEFAULT]);
- hotseatBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelLandscape,
- hotseatBorderSpaces[INDEX_DEFAULT]);
- hotseatBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
- R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
- hotseatBorderSpaces[INDEX_DEFAULT]);
-
hotseatBarBottomSpace[INDEX_DEFAULT] = a.getFloat(
R.styleable.ProfileDisplayOption_hotseatBarBottomSpace,
ResourcesCompat.getFloat(context.getResources(),
@@ -1133,7 +1108,6 @@
minCellSize[i].x *= w;
minCellSize[i].y *= w;
horizontalMargin[i] *= w;
- hotseatBorderSpaces[i] *= w;
hotseatBarBottomSpace[i] *= w;
hotseatQsbSpace[i] *= w;
allAppsCellSize[i].x *= w;
@@ -1158,7 +1132,6 @@
minCellSize[i].x += p.minCellSize[i].x;
minCellSize[i].y += p.minCellSize[i].y;
horizontalMargin[i] += p.horizontalMargin[i];
- hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
hotseatBarBottomSpace[i] += p.hotseatBarBottomSpace[i];
hotseatQsbSpace[i] += p.hotseatQsbSpace[i];
allAppsCellSize[i].x += p.allAppsCellSize[i].x;
diff --git a/tests/Android.bp b/tests/Android.bp
index 54cded0..1584308 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -24,6 +24,18 @@
"src/**/*.java",
"src/**/*.kt"
],
+ exclude_srcs: [
+ ":launcher-non-quickstep-tests-src"
+ ],
+}
+
+// Source code used for non-quickstep tests
+filegroup {
+ name: "launcher-non-quickstep-tests-src",
+ srcs: [
+ "src/com/android/launcher3/nonquickstep/**/*.java",
+ "src/com/android/launcher3/nonquickstep/**/*.kt",
+ ],
}
// Source code used for oop test helpers
@@ -84,6 +96,7 @@
name: "Launcher3Tests",
srcs: [
":launcher-tests-src",
+ ":launcher-non-quickstep-tests-src",
],
static_libs: ["Launcher3TestLib"],
libs: [
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 7046782..cf6be7f 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -114,7 +114,6 @@
PointF(16f, 16f)
).toTypedArray()
folderBorderSpace = 16f
- hotseatBorderSpaces = FloatArray(4) { 16f }
inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_5
horizontalMargin = FloatArray(4) { 22f }
@@ -136,8 +135,6 @@
numShownHotseatIcons = 4
- numShrunkenHotseatIcons = 4
-
numDatabaseHotseatIcons = 4
hotseatColumnSpan = IntArray(4) { 4 }
@@ -200,7 +197,6 @@
PointF(16f, 64f),
PointF(16f, 64f)
).toTypedArray()
- hotseatBorderSpaces = floatArrayOf(58f, 50.4f, 58f, 58f)
inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5
horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f)
@@ -222,8 +218,6 @@
numShownHotseatIcons = 6
- numShrunkenHotseatIcons = 5
-
numDatabaseHotseatIcons = 6
hotseatColumnSpan = intArrayOf(6, 4, 6, 6)
@@ -297,7 +291,6 @@
PointF(20f, 20f)
).toTypedArray()
folderBorderSpace = 16f
- hotseatBorderSpaces = floatArrayOf(36f, 36f, 18f, 23.3f)
inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_4
horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f)
@@ -319,8 +312,6 @@
numShownHotseatIcons = 6
- numShrunkenHotseatIcons = 6
-
numDatabaseHotseatIcons = 6
hotseatColumnSpan = IntArray(4) { 6 }
diff --git a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
deleted file mode 100644
index 95651ca..0000000
--- a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2022 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
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY
-import com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE
-import com.android.launcher3.InvariantDeviceProfile.TYPE_TABLET
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Test for [DeviceProfile]
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class HotseatShownIconsTest : DeviceProfileBaseTest() {
-
- @Test
- fun hotseat_size_is_shrunk_if_needed_when_large_screen() {
- initializeVarsForTablet(isLandscape = true)
- inv = inv!!.apply {
- deviceType = TYPE_MULTI_DISPLAY
- inlineQsb = booleanArrayOf(
- false,
- false,
- false,
- true // two panels landscape
- )
- }
- useTwoPanels = true
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- /**
- * For consistency, the hotseat should shrink if any orientation on the device type has an
- * inline qsb
- */
- @Test
- fun hotseat_size_is_shrunk_even_in_portrait_when_large_screen() {
- initializeVarsForTablet()
- inv = inv!!.apply {
- deviceType = TYPE_MULTI_DISPLAY
- inlineQsb = booleanArrayOf(
- false,
- false,
- false,
- true // two panels landscape
- )
- }
- useTwoPanels = true
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- @Test
- fun hotseat_size_is_default_when_small_screen() {
- initializeVarsForPhone()
- inv = inv!!.apply {
- deviceType = TYPE_MULTI_DISPLAY
- }
- useTwoPanels = true
-
- val dp = newDP()
-
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseat_size_is_not_shrunk_on_gesture_tablet() {
- initializeVarsForTablet(isLandscape = true)
- inv = inv!!.apply {
- deviceType = TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numShownHotseatIcons = 6
- }
-
- isGestureMode = true
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- @Test
- fun hotseat_size_is_shrunk_if_needed_on_tablet() {
- initializeVarsForTablet(isLandscape = true)
- inv = inv!!.apply {
- deviceType = TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numShownHotseatIcons = 6
- }
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
- /**
- * For consistency, the hotseat should shrink if any orientation on the device type has an
- * inline qsb
- */
- @Test
- fun hotseat_size_is_shrunk_even_in_portrait_on_tablet() {
- initializeVarsForTablet()
- inv = inv!!.apply {
- deviceType = TYPE_TABLET
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numShownHotseatIcons = 6
- }
-
- isGestureMode = false
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(5)
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
- }
-
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
new file mode 100644
index 0000000..55520e8
--- /dev/null
+++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 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.nonquickstep
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.launcher3.util.WindowBounds
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HotseatWidthCalculationTest : DeviceProfileBaseTest() {
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated
+ * but it doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1800, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(145)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(177)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(177)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1445)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, and recalculating spaces
+ * it still needs to remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_portrait() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1300, 2560), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(94)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(121)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(121)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1058)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, the space needs to be recalculated
+ * but it doesn't need to change QSB width or remove icons
+ */
+ @Test
+ fun distribute_border_space_when_space_is_enough_landscape() {
+ initializeVarsForTwoPanel(isGestureMode = false, isLandscape = true)
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(105)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(370)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(370)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1467)
+ }
+
+ /**
+ * This is a case when the hotseat spans a certain amount of columns
+ * and the nav buttons push the hotseat to the side, but not enough to change the border space.
+ */
+ @Test
+ fun nav_buttons_dont_interfere_with_required_hotseat_width() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ inv?.apply {
+ hotseatColumnSpan = IntArray(4) { 4 }
+ inlineQsb = BooleanArray(4) { false }
+ }
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(100)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(668)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(668)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1224)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, the QSB width needs to be changed to fit
+ */
+ @Test
+ fun decrease_qsb_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2460, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(96)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(643)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(643)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1174)
+ }
+
+ /**
+ * This is a case when after setting the hotseat, changing QSB width, and recalculating spaces
+ * it still needs to remove icons for everything to fit
+ */
+ @Test
+ fun decrease_num_of_icons_when_not_enough_space_landscape() {
+ initializeVarsForTablet(isGestureMode = false, isLandscape = true)
+ windowBounds = WindowBounds(Rect(0, 0, 2260, 1600), Rect(0, 104, 0, 0))
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(89)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(589)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(589)
+
+ assertThat(dp.isQsbInline).isFalse()
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1081)
+ }
+}