Add new attribute to hotseat specs

Edge padding is used as the space between hotseat and the edge of the screen. In most cases it is used in a height spec, but for vertical bar, i.e. when a handheld is in landscape, it is used a width spec.

Fix: 292204436
Test: CalculatedHotseatSpecTest
Test: HotseatSpecsTest
Test: DeviceProfileResponsiveAlternativeDisplaysDumpTest
Test: DeviceProfileResponsiveDumpTest
Flag: ENABLE_RESPONSIVE_WORKSPACE
Change-Id: Ib75ffcdd33925ee9ef156dcb70eefd133c9b458b
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a48c928..0a5524b 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -205,9 +205,11 @@
     public int hotseatBarEndOffset;
     public int hotseatQsbSpace;
     public int springLoadedHotseatBarTopMarginPx;
-    // Start is the side next to the nav bar, end is the side next to the workspace
-    public final int hotseatBarSidePaddingStartPx;
-    public final int hotseatBarSidePaddingEndPx;
+    // These 2 values are only used for isVerticalBar
+    // Padding between edge of screen and hotseat
+    public final int mHotseatBarEdgePaddingPx;
+    // Space between hotseat and workspace (not used in responsive)
+    public final int mHotseatBarWorkspaceSpacePx;
     public int hotseatQsbWidth; // only used when isQsbInline
     public final int hotseatQsbHeight;
     public final int hotseatQsbVisualHeight;
@@ -497,46 +499,56 @@
         numShownAllAppsColumns =
                 isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
 
-        int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+        int hotseatBarBottomSpace;
         int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin);
 
         if (mIsResponsiveGrid) {
             HotseatSpecs hotseatSpecs =
                     HotseatSpecs.create(new ResourceHelper(context,
                             isTwoPanels ? inv.hotseatSpecsTwoPanelId : inv.hotseatSpecsId));
-            mResponsiveHotseatSpec = hotseatSpecs.getCalculatedHeightSpec(heightPx);
+            mResponsiveHotseatSpec =
+                    isVerticalBarLayout() ? hotseatSpecs.getCalculatedWidthSpec(widthPx)
+                            : hotseatSpecs.getCalculatedHeightSpec(heightPx);
             hotseatQsbSpace = mResponsiveHotseatSpec.getHotseatQsbSpace();
+            hotseatBarBottomSpace =
+                    isVerticalBarLayout() ? 0 : mResponsiveHotseatSpec.getEdgePadding();
+            mHotseatBarEdgePaddingPx =
+                    isVerticalBarLayout() ? mResponsiveHotseatSpec.getEdgePadding() : 0;
+            mHotseatBarWorkspaceSpacePx = 0;
         } else {
             hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics);
+            hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+            mHotseatBarEdgePaddingPx =
+                    isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
+            mHotseatBarWorkspaceSpacePx =
+                    res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         }
 
-        // Have a little space between the inset and the QSB
-        if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
-            int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
+        if (!isVerticalBarLayout()) {
+            // Have a little space between the inset and the QSB
+            if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
+                int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
 
-            // Only change the spaces if there is space
-            if (availableSpace > 0) {
-                // Make sure there is enough space between hotseat/QSB and QSB/navBar
-                if (availableSpace < minQsbMargin * 2) {
-                    minQsbMargin = availableSpace / 2;
-                    hotseatQsbSpace = minQsbMargin;
-                } else {
-                    hotseatQsbSpace -= minQsbMargin;
+                // Only change the spaces if there is space
+                if (availableSpace > 0) {
+                    // Make sure there is enough space between hotseat/QSB and QSB/navBar
+                    if (availableSpace < minQsbMargin * 2) {
+                        minQsbMargin = availableSpace / 2;
+                        hotseatQsbSpace = minQsbMargin;
+                    } else {
+                        hotseatQsbSpace -= minQsbMargin;
+                    }
                 }
-            }
-            hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin;
+                hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin;
 
-        } else {
-            hotseatBarBottomSpacePx = hotseatBarBottomSpace;
+            } else {
+                hotseatBarBottomSpacePx = hotseatBarBottomSpace;
+            }
         }
 
         springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
                 R.dimen.spring_loaded_hotseat_top_margin);
-        hotseatBarSidePaddingEndPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
-        // Add a bit of space between nav bar and hotseat in vertical bar layout.
-        hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
-        updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
+        updateHotseatSizes(pxFromDp(inv.iconSize[mTypeIndex], mMetrics));
         if (areNavButtonsInline && !isPhone) {
             inlineNavButtonsEndSpacingPx =
                     res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing);
@@ -562,10 +574,9 @@
             int availableResponsiveWidth =
                     availableWidthPx - (isVerticalBarLayout() ? hotseatBarSizePx : 0);
             int numColumns = getPanelCount() * inv.numColumns;
-            // don't use availableHeightPx because it subtracts bottom padding,
-            // but the workspace go behind it
-            int availableResponsiveHeight =
-                    heightPx - mInsets.top - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
+            // don't use availableHeightPx because it subtracts mInsets.bottom
+            int availableResponsiveHeight = heightPx - mInsets.top
+                            - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
             mResponsiveWidthSpec = workspaceSpecs.getCalculatedWidthSpec(numColumns,
                     availableResponsiveWidth);
             mResponsiveHeightSpec = workspaceSpecs.getCalculatedHeightSpec(inv.numRows,
@@ -738,8 +749,8 @@
         hotseatCellHeightPx = getIconSizeWithOverlap(hotseatIconSizePx);
 
         if (isVerticalBarLayout()) {
-            hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
-                    + hotseatBarSidePaddingEndPx;
+            hotseatBarSizePx = hotseatIconSizePx + mHotseatBarEdgePaddingPx
+                    + mHotseatBarWorkspaceSpacePx;
         } else if (isQsbInline) {
             hotseatBarSizePx = Math.max(hotseatIconSizePx, hotseatQsbVisualHeight)
                     + hotseatBarBottomSpacePx;
@@ -1007,6 +1018,7 @@
                 iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
             }
 
+            // TODO(b/296400197): isVerticalBar shouldn't show labels anymore
             iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
             int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
             int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
@@ -1491,7 +1503,8 @@
         if (isVerticalBarLayout()) {
             if (mIsResponsiveGrid) {
                 padding.top = mResponsiveHeightSpec.getStartPaddingPx();
-                padding.bottom = mResponsiveHeightSpec.getEndPaddingPx();
+                padding.bottom = Math.max(0,
+                        mResponsiveHeightSpec.getEndPaddingPx() - mInsets.bottom);
                 if (isSeascape()) {
                     padding.left = hotseatBarSizePx + mResponsiveWidthSpec.getEndPaddingPx();
                     padding.right = mResponsiveWidthSpec.getStartPaddingPx();
@@ -1504,9 +1517,9 @@
                 padding.bottom = edgeMarginPx;
                 if (isSeascape()) {
                     padding.left = hotseatBarSizePx;
-                    padding.right = hotseatBarSidePaddingStartPx;
+                    padding.right = mHotseatBarEdgePaddingPx;
                 } else {
-                    padding.left = hotseatBarSidePaddingStartPx;
+                    padding.left = mHotseatBarEdgePaddingPx;
                     padding.right = hotseatBarSizePx;
                 }
             }
@@ -1560,11 +1573,11 @@
                     + diffOverlapFactor), 0);
 
             if (isSeascape()) {
-                hotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
-                        hotseatBarSidePaddingEndPx, paddingBottom);
+                hotseatBarPadding.set(mInsets.left + mHotseatBarEdgePaddingPx, paddingTop,
+                        mHotseatBarWorkspaceSpacePx, paddingBottom);
             } else {
-                hotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
-                        mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
+                hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
+                        mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
             }
         } else if (isTaskbarPresent) {
             // Center the QSB vertically with hotseat
@@ -1910,10 +1923,10 @@
         writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
         writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
         writer.println(prefix + pxToDpStr("hotseatBarBottomSpacePx", hotseatBarBottomSpacePx));
-        writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
-                hotseatBarSidePaddingStartPx));
-        writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
-                hotseatBarSidePaddingEndPx));
+        writer.println(prefix + pxToDpStr("mHotseatBarEdgePaddingPx",
+                mHotseatBarEdgePaddingPx));
+        writer.println(prefix + pxToDpStr("mHotseatBarWorkspaceSpacePx",
+                mHotseatBarWorkspaceSpacePx));
         writer.println(prefix + pxToDpStr("hotseatBarEndOffset", hotseatBarEndOffset));
         writer.println(prefix + pxToDpStr("hotseatQsbSpace", hotseatQsbSpace));
         writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight));
diff --git a/src/com/android/launcher3/responsive/HotseatSpecs.kt b/src/com/android/launcher3/responsive/HotseatSpecs.kt
index 482508d..d578b08 100644
--- a/src/com/android/launcher3/responsive/HotseatSpecs.kt
+++ b/src/com/android/launcher3/responsive/HotseatSpecs.kt
@@ -21,14 +21,20 @@
 import com.android.launcher3.R
 import com.android.launcher3.util.ResourceHelper
 
-class HotseatSpecs(val specs: List<HotseatSpec>) {
+class HotseatSpecs(val widthSpecs: List<HotseatSpec>, val heightSpecs: List<HotseatSpec>) {
 
     fun getCalculatedHeightSpec(availableHeight: Int): CalculatedHotseatSpec {
-        val spec = specs.firstOrNull { availableHeight <= it.maxAvailableSize }
+        val spec = heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
         check(spec != null) { "No available height spec found within $availableHeight." }
         return CalculatedHotseatSpec(availableHeight, spec)
     }
 
+    fun getCalculatedWidthSpec(availableWidth: Int): CalculatedHotseatSpec {
+        val spec = widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize }
+        check(spec != null) { "No available width spec found within $availableWidth." }
+        return CalculatedHotseatSpec(availableWidth, spec)
+    }
+
     companion object {
         private const val XML_HOTSEAT_SPEC = "hotseatSpec"
 
@@ -36,7 +42,9 @@
         fun create(resourceHelper: ResourceHelper): HotseatSpecs {
             val parser = ResponsiveSpecsParser(resourceHelper)
             val specs = parser.parseXML(XML_HOTSEAT_SPEC, ::HotseatSpec)
-            return HotseatSpecs(specs.filter { it.specType == ResponsiveSpec.SpecType.HEIGHT })
+            val (widthSpecs, heightSpecs) =
+                specs.partition { it.specType == ResponsiveSpec.SpecType.WIDTH }
+            return HotseatSpecs(widthSpecs, heightSpecs)
         }
     }
 }
@@ -44,7 +52,8 @@
 data class HotseatSpec(
     val maxAvailableSize: Int,
     val specType: ResponsiveSpec.SpecType,
-    val hotseatQsbSpace: SizeSpec
+    val hotseatQsbSpace: SizeSpec,
+    val edgePadding: SizeSpec
 ) {
 
     init {
@@ -63,7 +72,8 @@
                         R.styleable.ResponsiveSpec_specType,
                         ResponsiveSpec.SpecType.HEIGHT.ordinal
                     )],
-        hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE)
+        hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE),
+        edgePadding = specs.getOrError(SizeSpec.XmlTags.EDGE_PADDING)
     )
 
     fun isValid(): Boolean {
@@ -82,7 +92,10 @@
     }
 
     private fun allSpecsAreValid(): Boolean {
-        return hotseatQsbSpace.isValid() && hotseatQsbSpace.onlyFixedSize()
+        return hotseatQsbSpace.isValid() &&
+            hotseatQsbSpace.onlyFixedSize() &&
+            edgePadding.isValid() &&
+            edgePadding.onlyFixedSize()
     }
 
     companion object {
@@ -95,13 +108,18 @@
     var hotseatQsbSpace: Int = 0
         private set
 
+    var edgePadding: Int = 0
+        private set
+
     init {
         hotseatQsbSpace = spec.hotseatQsbSpace.getCalculatedValue(availableSpace)
+        edgePadding = spec.edgePadding.getCalculatedValue(availableSpace)
     }
 
     override fun hashCode(): Int {
         var result = availableSpace.hashCode()
         result = 31 * result + hotseatQsbSpace.hashCode()
+        result = 31 * result + edgePadding.hashCode()
         result = 31 * result + spec.hashCode()
         return result
     }
@@ -110,12 +128,14 @@
         return other is CalculatedHotseatSpec &&
             availableSpace == other.availableSpace &&
             hotseatQsbSpace == other.hotseatQsbSpace &&
+            edgePadding == other.edgePadding &&
             spec == other.spec
     }
 
     override fun toString(): String {
         return "${this::class.simpleName}(" +
             "availableSpace=$availableSpace, hotseatQsbSpace=$hotseatQsbSpace, " +
+            "edgePadding=$edgePadding, " +
             "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
             ")"
     }
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
index c868c9f..2db843b 100644
--- a/src/com/android/launcher3/responsive/SizeSpec.kt
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -121,6 +121,7 @@
         const val GUTTER = "gutter"
         const val CELL_SIZE = "cellSize"
         const val HOTSEAT_QSB_SPACE = "hotseatQsbSpace"
+        const val EDGE_PADDING = "edgePadding"
     }
 
     companion object {