Make inline qsb part of grid attrs

This should make it more configurable, and be independent of the screen size set by the user.

Fix: 223726518
Test: atest Launcher3Tests:HotseatSizeTest
Test: atest Launcher3Tests:InlineQsbTest
Change-Id: If04f3fb0f556103f60b580c757a9fc06da561516
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c96a228..fcb9053 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -316,6 +316,18 @@
         <attr name="horizontalMarginTwoPanelLandscape" format="float"/>
         <!-- defaults to horizontalMargin if not specified -->
         <attr name="horizontalMarginTwoPanelPortrait" format="float"/>
+
+        <!-- By default all are false -->
+        <attr name="inlineQsb" format="integer" >
+            <!-- Enable on landscape only -->
+            <flag name="portrait" value="1" />
+            <!-- Enable on portrait only -->
+            <flag name="landscape" value="2" />
+            <!-- Enable on two panel portrait only -->
+            <flag name="twoPanelPortrait" value="4" />
+            <!-- Enable on two panel landscape only -->
+            <flag name="twoPanelLandscape" value="8" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 0c33bce..db31350 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,6 +16,10 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.InvariantDeviceProfile.INDEX_DEFAULT;
+import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE;
+import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
+import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
 import static com.android.launcher3.ResourceUtils.pxFromDp;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.pxFromSp;
@@ -58,7 +62,6 @@
 
     // Device properties
     public final boolean isTablet;
-    public final boolean isLargeTablet;
     public final boolean isPhone;
     public final boolean transposeLayoutWithOrientation;
     public final boolean isTwoPanels;
@@ -253,7 +256,6 @@
         // Determine device posture.
         mInfo = info;
         isTablet = info.isTablet(windowBounds);
-        isLargeTablet = info.isLargeTablet(windowBounds);
         isPhone = !isTablet;
         isTwoPanels = isTablet && useTwoPanels;
         isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS;
@@ -278,15 +280,15 @@
 
         if (isTwoPanels) {
             if (isLandscape) {
-                mTypeIndex = InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
+                mTypeIndex = INDEX_TWO_PANEL_LANDSCAPE;
             } else {
-                mTypeIndex = InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
+                mTypeIndex = INDEX_TWO_PANEL_PORTRAIT;
             }
         } else {
             if (isLandscape) {
-                mTypeIndex = InvariantDeviceProfile.INDEX_LANDSCAPE;
+                mTypeIndex = INDEX_LANDSCAPE;
             } else {
-                mTypeIndex = InvariantDeviceProfile.INDEX_DEFAULT;
+                mTypeIndex = INDEX_DEFAULT;
             }
         }
 
@@ -348,9 +350,12 @@
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
         hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
-        // Whether QSB might be inline in appropriate orientation (landscape).
-        boolean canQsbInline = isLargeTablet && hotseatQsbHeight > 0;
-        isQsbInline = canQsbInline && isLandscape;
+        // Whether QSB might be inline in appropriate orientation (e.g. landscape).
+        boolean canQsbInline = (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
+                || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
+                : inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
+                && 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.
@@ -388,7 +393,7 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
         hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
         updateHotseatIconSize(
-                pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics));
+                pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
 
         qsbBottomMarginOriginalPx = isScalableGrid
                 ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
@@ -812,11 +817,11 @@
 
     private void updateFolderCellSize(float scale, Resources res) {
         float invIconSizeDp = isVerticalBarLayout()
-                ? inv.iconSize[InvariantDeviceProfile.INDEX_LANDSCAPE]
-                : inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT];
+                ? inv.iconSize[INDEX_LANDSCAPE]
+                : inv.iconSize[INDEX_DEFAULT];
         folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
         folderChildTextSizePx =
-                pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, scale);
+                pxFromSp(inv.iconTextSize[INDEX_DEFAULT], mMetrics, scale);
         folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
 
         int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
@@ -1167,7 +1172,6 @@
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
 
         writer.println(prefix + "\tisTablet:" + isTablet);
-        writer.println(prefix + "\tisLargeTablet:" + isLargeTablet);
         writer.println(prefix + "\tisPhone:" + isPhone);
         writer.println(prefix + "\ttransposeLayoutWithOrientation:"
                 + transposeLayoutWithOrientation);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 219ed9e..9f367ef 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -167,6 +167,7 @@
     public String dbFile;
     public int defaultLayoutId;
     int demoModeLayoutId;
+    boolean[] inlineQsb = new boolean[COUNT_SIZES];
 
     /**
      * An immutable list of supported profiles.
@@ -250,6 +251,8 @@
                 COUNT_SIZES);
         System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
                 COUNT_SIZES);
+        System.arraycopy(defaultDisplayOption.inlineQsb, 0, result.inlineQsb, 0,
+                COUNT_SIZES);
 
         initGrid(context, myInfo, result, deviceType);
     }
@@ -371,6 +374,8 @@
             devicePaddings = new DevicePaddings(context, devicePaddingId);
         }
 
+        inlineQsb = displayOption.inlineQsb;
+
         // If the partner customization apk contains any grid overrides, apply them
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, metrics);
@@ -783,12 +788,18 @@
 
     @VisibleForTesting
     static final class DisplayOption {
+        private static final int INLINE_QSB_FOR_PORTRAIT = 1 << 0;
+        private static final int INLINE_QSB_FOR_LANDSCAPE = 1 << 1;
+        private static final int INLINE_QSB_FOR_TWO_PANEL_PORTRAIT = 1 << 2;
+        private static final int INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE = 1 << 3;
+        private static final int DONT_INLINE_QSB = 0;
 
         public final GridOption grid;
 
         private final float minWidthDps;
         private final float minHeightDps;
         private final boolean canBeDefault;
+        private final boolean[] inlineQsb = new boolean[COUNT_SIZES];
 
         private final PointF[] minCellSize = new PointF[COUNT_SIZES];
 
@@ -815,6 +826,19 @@
 
             canBeDefault = a.getBoolean(R.styleable.ProfileDisplayOption_canBeDefault, false);
 
+            int inlineForRotation = a.getInt(R.styleable.ProfileDisplayOption_inlineQsb,
+                    DONT_INLINE_QSB);
+            inlineQsb[INDEX_DEFAULT] =
+                    (inlineForRotation & INLINE_QSB_FOR_PORTRAIT) == INLINE_QSB_FOR_PORTRAIT;
+            inlineQsb[INDEX_LANDSCAPE] =
+                    (inlineForRotation & INLINE_QSB_FOR_LANDSCAPE) == INLINE_QSB_FOR_LANDSCAPE;
+            inlineQsb[INDEX_TWO_PANEL_PORTRAIT] =
+                    (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_PORTRAIT)
+                            == INLINE_QSB_FOR_TWO_PANEL_PORTRAIT;
+            inlineQsb[INDEX_TWO_PANEL_LANDSCAPE] =
+                    (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE)
+                            == INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE;
+
             float x;
             float y;
 
@@ -1004,6 +1028,7 @@
                 allAppsIconSizes[i] = 0;
                 allAppsIconTextSizes[i] = 0;
                 allAppsBorderSpaces[i] = new PointF();
+                inlineQsb[i] = false;
             }
         }
 
@@ -1046,6 +1071,7 @@
                 allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i];
                 allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
                 allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y;
+                inlineQsb[i] |= p.inlineQsb[i];
             }
 
             folderBorderSpace += p.folderBorderSpace;
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 8b4ff85..44a0ef4 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
-import static com.android.launcher3.util.window.WindowManagerProxy.MIN_LARGE_TABLET_WIDTH;
 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
@@ -349,13 +348,6 @@
         }
 
         /**
-         * Returns {@code true} if the bounds represent a large tablet.
-         */
-        public boolean isLargeTablet(WindowBounds bounds) {
-            return smallestSizeDp(bounds) >= MIN_LARGE_TABLET_WIDTH;
-        }
-
-        /**
          * Returns smallest size in dp for given bounds.
          */
         public float smallestSizeDp(WindowBounds bounds) {
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index ba3d981..71460f3 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -58,7 +58,6 @@
 public class WindowManagerProxy implements ResourceBasedOverride {
 
     public static final int MIN_TABLET_WIDTH = 600;
-    public static final int MIN_LARGE_TABLET_WIDTH = 720;
 
     public static final MainThreadInitializedObject<WindowManagerProxy> INSTANCE =
             forOverride(WindowManagerProxy.class, R.string.window_manager_proxy_class);
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
new file mode 100644
index 0000000..e598df9
--- /dev/null
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -0,0 +1,133 @@
+/*
+ * 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 android.content.Context
+import android.graphics.PointF
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.util.DisplayController.Info
+import com.android.launcher3.util.WindowBounds
+import org.junit.Before
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when` as whenever
+
+abstract class DeviceProfileBaseTest {
+
+    protected var context: Context? = null
+    protected var inv: InvariantDeviceProfile? = null
+    protected var info: Info = mock(Info::class.java)
+    protected var windowBounds: WindowBounds? = null
+    protected var isMultiWindowMode: Boolean = false
+    protected var transposeLayoutWithOrientation: Boolean = false
+    protected var useTwoPanels: Boolean = false
+    protected var isGestureMode: Boolean = true
+
+    @Before
+    fun setUp() {
+        context = ApplicationProvider.getApplicationContext()
+        // make sure to reset values
+        useTwoPanels = false
+        isGestureMode = true
+    }
+
+    protected fun newDP(): DeviceProfile = DeviceProfile(
+        context,
+        inv,
+        info,
+        windowBounds,
+        isMultiWindowMode,
+        transposeLayoutWithOrientation,
+        useTwoPanels,
+        isGestureMode
+    )
+
+    protected fun initializeVarsForPhone(isLandscape: Boolean = false) {
+        val (x, y) = if (isLandscape)
+            Pair(3120, 1440)
+        else
+            Pair(1440, 3120)
+
+        windowBounds = WindowBounds(x, y, x, y - 100, 0)
+
+        whenever(info.isTablet(any())).thenReturn(false)
+
+        inv = newScalableInvariantDeviceProfile()
+    }
+
+    protected fun initializeVarsForTablet(isLandscape: Boolean = false) {
+        val (x, y) = if (isLandscape)
+            Pair(2560, 1600)
+        else
+            Pair(1600, 2560)
+
+        windowBounds = WindowBounds(x, y, x, y - 100, 0)
+
+        whenever(info.isTablet(any())).thenReturn(true)
+
+        inv = newScalableInvariantDeviceProfile()
+    }
+
+    /**
+     * A very generic grid, just to make qsb tests work. For real calculations, make sure to use
+     * values that better represent a real grid.
+     */
+    protected fun newScalableInvariantDeviceProfile(): InvariantDeviceProfile =
+        InvariantDeviceProfile().apply {
+            isScalable = true
+            numColumns = 5
+            numRows = 5
+            numShownHotseatIcons = 5
+            numDatabaseHotseatIcons = 6
+            numShrunkenHotseatIcons = 4
+            horizontalMargin = FloatArray(4) { 22f }
+            borderSpaces = listOf(
+                PointF(16f, 16f),
+                PointF(16f, 16f),
+                PointF(16f, 16f),
+                PointF(16f, 16f)
+            ).toTypedArray()
+            allAppsBorderSpaces = listOf(
+                PointF(16f, 16f),
+                PointF(16f, 16f),
+                PointF(16f, 16f),
+                PointF(16f, 16f)
+            ).toTypedArray()
+            hotseatBorderSpaces = FloatArray(4) { 16f }
+            iconSize = FloatArray(4) { 56f }
+            allAppsIconSize = FloatArray(4) { 56f }
+            iconTextSize = FloatArray(4) { 14f }
+            allAppsIconTextSize = FloatArray(4) { 14f }
+            minCellSize = listOf(
+                PointF(64f, 83f),
+                PointF(64f, 83f),
+                PointF(64f, 83f),
+                PointF(64f, 83f)
+            ).toTypedArray()
+            allAppsCellSize = listOf(
+                PointF(64f, 83f),
+                PointF(64f, 83f),
+                PointF(64f, 83f),
+                PointF(64f, 83f)
+            ).toTypedArray()
+            inlineQsb = booleanArrayOf(
+                false,
+                false,
+                false,
+                false
+            )
+        }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/DeviceProfileTest.kt b/tests/src/com/android/launcher3/DeviceProfileTest.kt
deleted file mode 100644
index d1e91ed..0000000
--- a/tests/src/com/android/launcher3/DeviceProfileTest.kt
+++ /dev/null
@@ -1,219 +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 android.content.Context
-import android.graphics.PointF
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.util.DisplayController.Info
-import com.android.launcher3.util.WindowBounds
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.*
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileTest {
-
-    private var context: Context? = null
-    private var inv: InvariantDeviceProfile? = null
-    private var info: Info = mock(Info::class.java)
-    private var windowBounds: WindowBounds? = null
-    private var isMultiWindowMode: Boolean = false
-    private var transposeLayoutWithOrientation: Boolean = false
-    private var useTwoPanels: Boolean = false
-    private var isGestureMode: Boolean = true
-
-    @Before
-    fun setUp() {
-        context = ApplicationProvider.getApplicationContext()
-        // make sure to reset values
-        useTwoPanels = false
-    }
-
-    @Test
-    fun qsbWidth_is_match_parent_for_phones() {
-        initializeVarsForPhone()
-
-        val dp = DeviceProfile(
-            context,
-            inv,
-            info,
-            windowBounds,
-            isMultiWindowMode,
-            transposeLayoutWithOrientation,
-            useTwoPanels,
-            isGestureMode
-        )
-
-        assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.qsbWidth).isEqualTo(0)
-    }
-
-    @Test
-    fun qsbWidth_is_match_parent_for_tablet_portrait() {
-        initializeVarsForLargeTablet()
-
-        val dp = DeviceProfile(
-            context,
-            inv,
-            info,
-            windowBounds,
-            isMultiWindowMode,
-            transposeLayoutWithOrientation,
-            useTwoPanels,
-            isGestureMode
-        )
-
-        assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.qsbWidth).isEqualTo(0)
-    }
-
-    @Test
-    fun qsbWidth_has_size_for_large_tablet_landscape() {
-        initializeVarsForLargeTablet(true)
-
-        val dp = DeviceProfile(
-            context,
-            inv,
-            info,
-            windowBounds,
-            isMultiWindowMode,
-            transposeLayoutWithOrientation,
-            useTwoPanels,
-            isGestureMode
-        )
-
-        if (dp.hotseatQsbHeight > 0) {
-            assertThat(dp.isQsbInline).isTrue()
-            assertThat(dp.qsbWidth).isGreaterThan(0)
-        } else {
-            assertThat(dp.isQsbInline).isFalse()
-            assertThat(dp.qsbWidth).isEqualTo(0)
-        }
-    }
-
-    /**
-     * This test is to make sure that two panels don't inline the QSB as tablets do
-     */
-    @Test
-    fun qsbWidth_is_match_parent_for_small_two_panel_landscape() {
-        initializeVarsForSmallTablet(true)
-        useTwoPanels = true
-
-        val dp = DeviceProfile(
-            context,
-            inv,
-            info,
-            windowBounds,
-            isMultiWindowMode,
-            transposeLayoutWithOrientation,
-            useTwoPanels,
-            isGestureMode
-        )
-
-        assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.qsbWidth).isEqualTo(0)
-    }
-
-    private fun initializeVarsForPhone(isLandscape: Boolean = false) {
-        val (x, y) = if (isLandscape)
-            Pair(3120, 1440)
-        else
-            Pair(1440, 3120)
-
-        windowBounds = WindowBounds(x, y, x, y - 100, 0)
-
-        `when`(info.isTablet(any())).thenReturn(false)
-        `when`(info.isLargeTablet(any())).thenReturn(false)
-
-        scalableInvariantDeviceProfile()
-    }
-
-    private fun initializeVarsForSmallTablet(isLandscape: Boolean = false) {
-        val (x, y) = if (isLandscape)
-            Pair(2560, 1600)
-        else
-            Pair(1600, 2560)
-
-        windowBounds = WindowBounds(x, y, x, y - 100, 0)
-
-        `when`(info.isTablet(any())).thenReturn(true)
-        `when`(info.isLargeTablet(any())).thenReturn(false)
-
-        scalableInvariantDeviceProfile()
-    }
-
-    private fun initializeVarsForLargeTablet(isLandscape: Boolean = false) {
-        val (x, y) = if (isLandscape)
-            Pair(2560, 1600)
-        else
-            Pair(1600, 2560)
-
-        windowBounds = WindowBounds(x, y, x, y - 100, 0)
-
-        `when`(info.isTablet(any())).thenReturn(true)
-        `when`(info.isLargeTablet(any())).thenReturn(true)
-
-        scalableInvariantDeviceProfile()
-    }
-
-    /**
-     * A very generic grid, just to make qsb tests work. For real calculations, make sure to use
-     * values that better represent a real grid.
-     */
-    private fun scalableInvariantDeviceProfile() {
-        inv = InvariantDeviceProfile().apply {
-            isScalable = true
-            numColumns = 5
-            numRows = 5
-            horizontalMargin = FloatArray(4) { 22f }
-            borderSpaces = listOf(
-                PointF(16f, 16f),
-                PointF(16f, 16f),
-                PointF(16f, 16f),
-                PointF(16f, 16f)
-            ).toTypedArray()
-            allAppsBorderSpaces = listOf(
-                PointF(16f, 16f),
-                PointF(16f, 16f),
-                PointF(16f, 16f),
-                PointF(16f, 16f)
-            ).toTypedArray()
-            hotseatBorderSpaces = FloatArray(4) { 16f }
-            iconSize = FloatArray(4) { 56f }
-            allAppsIconSize = FloatArray(4) { 56f }
-            iconTextSize = FloatArray(4) { 14f }
-            allAppsIconTextSize = FloatArray(4) { 14f }
-            minCellSize = listOf(
-                PointF(64f, 83f),
-                PointF(64f, 83f),
-                PointF(64f, 83f),
-                PointF(64f, 83f)
-            ).toTypedArray()
-            allAppsCellSize = listOf(
-                PointF(64f, 83f),
-                PointF(64f, 83f),
-                PointF(64f, 83f),
-                PointF(64f, 83f)
-            ).toTypedArray()
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/HotseatSizeTest.kt b/tests/src/com/android/launcher3/HotseatSizeTest.kt
new file mode 100644
index 0000000..ca697d7
--- /dev/null
+++ b/tests/src/com/android/launcher3/HotseatSizeTest.kt
@@ -0,0 +1,186 @@
+/*
+ * 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
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * Test for [DeviceProfile]
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HotseatSizeTest : DeviceProfileBaseTest() {
+
+    @Test
+    fun hotseat_size_is_normal_for_handhelds() {
+        initializeVarsForPhone()
+        inv = newScalableInvariantDeviceProfile().apply {
+            deviceType = TYPE_PHONE
+        }
+
+        val dp = newDP()
+
+        assertThat(dp.isQsbInline).isFalse()
+        assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+    }
+
+    @Test
+    fun hotseat_size_is_max_for_foldables() {
+        initializeVarsForTablet(isLandscape = true)
+        inv = newScalableInvariantDeviceProfile().apply {
+            deviceType = TYPE_MULTI_DISPLAY
+        }
+        useTwoPanels = true
+
+        val dp = newDP()
+
+        assertThat(dp.isQsbInline).isFalse()
+        assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+    }
+
+    @Test
+    fun hotseat_size_is_shrunk_if_needed() {
+        initializeVarsForTablet(isLandscape = true)
+        inv = newScalableInvariantDeviceProfile().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(4)
+        } 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() {
+        initializeVarsForTablet()
+        inv = newScalableInvariantDeviceProfile().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(4)
+        } else { // Launcher3 doesn't have QSB height
+            assertThat(dp.isQsbInline).isFalse()
+            assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+        }
+    }
+
+    @Test
+    fun hotseat_size_is_default_when_folded() {
+        initializeVarsForPhone()
+        inv = newScalableInvariantDeviceProfile().apply {
+            deviceType = TYPE_MULTI_DISPLAY
+        }
+        useTwoPanels = true
+
+        val dp = newDP()
+
+        assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+    }
+
+    @Test
+    fun hotseat_size_is_shrunk_if_needed_on_tablet() {
+        initializeVarsForTablet(isLandscape = true)
+        inv = newScalableInvariantDeviceProfile().apply {
+            deviceType = TYPE_TABLET
+            inlineQsb = booleanArrayOf(
+                false,
+                true, // landscape
+                false,
+                false
+            )
+        }
+
+        isGestureMode = false
+        val dp = newDP()
+
+        if (dp.hotseatQsbHeight > 0) {
+            assertThat(dp.isQsbInline).isTrue()
+            assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+        } else { // Launcher3 doesn't have QSB height
+            assertThat(dp.isQsbInline).isFalse()
+            assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+        }
+    }
+
+    /**
+     * 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 = newScalableInvariantDeviceProfile().apply {
+            deviceType = TYPE_TABLET
+            inlineQsb = booleanArrayOf(
+                false,
+                true, // landscape
+                false,
+                false
+            )
+        }
+
+        isGestureMode = false
+        val dp = newDP()
+
+        if (dp.hotseatQsbHeight > 0) {
+            assertThat(dp.isQsbInline).isFalse()
+            assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+        } else { // Launcher3 doesn't have QSB height
+            assertThat(dp.isQsbInline).isFalse()
+            assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt
new file mode 100644
index 0000000..e00dca8
--- /dev/null
+++ b/tests/src/com/android/launcher3/InlineQsbTest.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test for [DeviceProfile]
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InlineQsbTest : DeviceProfileBaseTest() {
+
+    @Test
+    fun qsbWidth_is_match_parent_for_phones() {
+        initializeVarsForPhone()
+
+        val dp = newDP()
+
+        assertThat(dp.isQsbInline).isFalse()
+        assertThat(dp.qsbWidth).isEqualTo(0)
+    }
+
+    @Test
+    fun qsbWidth_is_match_parent_for_tablet_portrait() {
+        initializeVarsForTablet()
+        inv = newScalableInvariantDeviceProfile().apply {
+            inlineQsb = booleanArrayOf(
+                false,
+                true, // landscape
+                false,
+                false
+            )
+        }
+
+        val dp = DeviceProfile(
+            context,
+            inv,
+            info,
+            windowBounds,
+            isMultiWindowMode,
+            transposeLayoutWithOrientation,
+            useTwoPanels,
+            isGestureMode
+        )
+
+        assertThat(dp.isQsbInline).isFalse()
+        assertThat(dp.qsbWidth).isEqualTo(0)
+    }
+
+    @Test
+    fun qsbWidth_has_size_for_tablet_landscape() {
+        initializeVarsForTablet(isLandscape = true)
+        inv = newScalableInvariantDeviceProfile().apply {
+            inlineQsb = booleanArrayOf(
+                false,
+                true, // landscape
+                false,
+                false
+            )
+        }
+
+        val dp = newDP()
+
+        if (dp.hotseatQsbHeight > 0) {
+            assertThat(dp.isQsbInline).isTrue()
+            assertThat(dp.qsbWidth).isGreaterThan(0)
+        } else { // Launcher3 doesn't have QSB height
+            assertThat(dp.isQsbInline).isFalse()
+            assertThat(dp.qsbWidth).isEqualTo(0)
+        }
+    }
+
+    /**
+     * This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support
+     */
+    @Test
+    fun qsbWidth_is_match_parent_for_tablet_landscape_without_inline() {
+        initializeVarsForTablet(isLandscape = true)
+        useTwoPanels = true
+
+        val dp = newDP()
+
+        assertThat(dp.isQsbInline).isFalse()
+        assertThat(dp.qsbWidth).isEqualTo(0)
+    }
+
+}
\ No newline at end of file