Use less icons in hotseat when in 3 button nav for tablet
- We have less space on tablets when 3 button nav is enabled because QSB is now inline with the icons. This creates a new attribute to define how many icons should be shown when in that mode. This could be used for other grids in the future as well.
- InvariantDeviceProfile now listens for nav mode changes
Fixes 214882090, 221420204
Test: manual
Change-Id: I012432a1a322c4e5505e46a1198c841ab124aaa6
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 99a337e..620eb7b 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -152,6 +152,9 @@
<!-- 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" />
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 97c0f38..cd9bbf7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -34,7 +34,6 @@
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.DevicePaddings.DevicePadding;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -59,6 +58,7 @@
// Device properties
public final boolean isTablet;
+ public final boolean isLargeTablet;
public final boolean isPhone;
public final boolean transposeLayoutWithOrientation;
public final boolean isTwoPanels;
@@ -67,6 +67,7 @@
// Device properties in current orientation
public final boolean isLandscape;
public final boolean isMultiWindowMode;
+ public final boolean isGestureMode;
public final int windowX;
public final int windowY;
@@ -229,12 +230,13 @@
/** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
- boolean useTwoPanels) {
+ boolean useTwoPanels, boolean isGestureMode) {
this.inv = inv;
this.isLandscape = windowBounds.isLandscape();
this.isMultiWindowMode = isMultiWindowMode;
this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
+ this.isGestureMode = isGestureMode;
windowX = windowBounds.bounds.left;
windowY = windowBounds.bounds.top;
@@ -243,6 +245,7 @@
// 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;
@@ -343,9 +346,15 @@
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
- isQsbInline = isTablet && isLandscape && !isTwoPanels && hotseatQsbHeight > 0;
- numShownHotseatIcons =
- isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+ isQsbInline = isLargeTablet && isLandscape && hotseatQsbHeight > 0;
+
+ if (isTaskbarPresent && !isGestureMode && isQsbInline) {
+ numShownHotseatIcons = inv.numShrunkenHotseatIcons;
+ } else {
+ numShownHotseatIcons =
+ isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+ }
+
numShownAllAppsColumns =
isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
hotseatBarSizeExtraSpacePx = 0;
@@ -540,7 +549,8 @@
return new Builder(context, inv, mInfo)
.setWindowBounds(bounds)
.setUseTwoPanels(isTwoPanels)
- .setMultiWindowMode(isMultiWindowMode);
+ .setMultiWindowMode(isMultiWindowMode)
+ .setGestureMode(isGestureMode);
}
public DeviceProfile copy(Context context) {
@@ -1081,9 +1091,11 @@
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);
+ writer.println(prefix + "\tisGestureMode:" + isGestureMode);
writer.println(prefix + "\tisLandscape:" + isLandscape);
writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
@@ -1266,6 +1278,7 @@
private boolean mIsMultiWindowMode = false;
private Boolean mTransposeLayoutWithOrientation;
+ private Boolean mIsGestureMode;
public Builder(Context context, InvariantDeviceProfile inv, Info info) {
mContext = context;
@@ -1294,6 +1307,11 @@
return this;
}
+ public Builder setGestureMode(boolean isGestureMode) {
+ mIsGestureMode = isGestureMode;
+ return this;
+ }
+
public DeviceProfile build() {
if (mWindowBounds == null) {
throw new IllegalArgumentException("Window bounds not set");
@@ -1301,8 +1319,11 @@
if (mTransposeLayoutWithOrientation == null) {
mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
}
- return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds,
- mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels);
+ if (mIsGestureMode == null) {
+ mIsGestureMode = DisplayController.getNavigationMode(mContext).hasGestures;
+ }
+ return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mIsMultiWindowMode,
+ mTransposeLayoutWithOrientation, mUseTwoPanels, mIsGestureMode);
}
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index dfe4bb0..886c657 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -138,6 +139,11 @@
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.
@@ -188,7 +194,8 @@
DisplayController.INSTANCE.get(context).setPriorityListener(
(displayContext, info, flags) -> {
- if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
+ if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS
+ | CHANGE_NAVIGATION_MODE)) != 0) {
onConfigChanged(displayContext);
}
});
@@ -336,6 +343,7 @@
horizontalMargin = displayOption.horizontalMargin;
numShownHotseatIcons = closestProfile.numHotseatIcons;
+ numShrunkenHotseatIcons = closestProfile.numShrunkenHotseatIcons;
numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
@@ -365,7 +373,8 @@
for (WindowBounds bounds : displayInfo.supportedBounds) {
localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
.setUseTwoPanels(deviceType == TYPE_MULTI_DISPLAY)
- .setWindowBounds(bounds).build());
+ .setWindowBounds(bounds)
+ .build());
// Wallpaper size should be the maximum of the all possible sizes Launcher expects
int displayWidth = bounds.bounds.width();
@@ -691,6 +700,7 @@
private final int numAllAppsColumns;
private final int numDatabaseAllAppsColumns;
private final int numHotseatIcons;
+ private final int numShrunkenHotseatIcons;
private final int numDatabaseHotseatIcons;
private final String dbFile;
@@ -727,6 +737,8 @@
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);
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 5f41192..f944d3c 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -26,6 +26,7 @@
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.WindowManagerCompat.MIN_LARGE_TABLET_WIDTH;
import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
import static java.util.Collections.emptyMap;
@@ -369,12 +370,20 @@
}
/**
- * Returns true if the bounds represent a tablet
+ * Returns {@code true} if the bounds represent a tablet.
*/
public boolean isTablet(WindowBounds bounds) {
return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
densityDpi) >= MIN_TABLET_WIDTH;
}
+
+ /**
+ * Returns {@code true} if the bounds represent a large tablet.
+ */
+ public boolean isLargeTablet(WindowBounds bounds) {
+ return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
+ densityDpi) >= MIN_LARGE_TABLET_WIDTH;
+ }
}
/**
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
index e1b9478..873a518 100644
--- a/src/com/android/launcher3/util/WindowManagerCompat.java
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -45,6 +45,7 @@
public class WindowManagerCompat {
public static final int MIN_TABLET_WIDTH = 600;
+ public static final int MIN_LARGE_TABLET_WIDTH = 720;
/**
* Returns a set of supported render sizes for a internal display.
diff --git a/tests/src/com/android/launcher3/DeviceProfileTest.kt b/tests/src/com/android/launcher3/DeviceProfileTest.kt
index 6c99a21..60046a0 100644
--- a/tests/src/com/android/launcher3/DeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileTest.kt
@@ -39,6 +39,7 @@
private var isMultiWindowMode: Boolean = false
private var transposeLayoutWithOrientation: Boolean = false
private var useTwoPanels: Boolean = false
+ private var isGestureMode: Boolean = true
@Before
fun setUp() {
@@ -58,7 +59,8 @@
windowBounds,
isMultiWindowMode,
transposeLayoutWithOrientation,
- useTwoPanels
+ useTwoPanels,
+ isGestureMode
)
assertThat(dp.isQsbInline).isFalse()
@@ -67,7 +69,7 @@
@Test
fun qsbWidth_is_match_parent_for_tablet_portrait() {
- initializeVarsForTablet()
+ initializeVarsForLargeTablet()
val dp = DeviceProfile(
context,
@@ -76,7 +78,8 @@
windowBounds,
isMultiWindowMode,
transposeLayoutWithOrientation,
- useTwoPanels
+ useTwoPanels,
+ isGestureMode
)
assertThat(dp.isQsbInline).isFalse()
@@ -84,8 +87,8 @@
}
@Test
- fun qsbWidth_has_size_for_tablet_landscape() {
- initializeVarsForTablet(true)
+ fun qsbWidth_has_size_for_large_tablet_landscape() {
+ initializeVarsForLargeTablet(true)
val dp = DeviceProfile(
context,
@@ -94,7 +97,8 @@
windowBounds,
isMultiWindowMode,
transposeLayoutWithOrientation,
- useTwoPanels
+ useTwoPanels,
+ isGestureMode
)
if (dp.hotseatQsbHeight > 0) {
@@ -110,8 +114,8 @@
* This test is to make sure that two panels don't inline the QSB as tablets do
*/
@Test
- fun qsbWidth_is_match_parent_for_two_panel_landscape() {
- initializeVarsForTablet(true)
+ fun qsbWidth_is_match_parent_for_small_two_panel_landscape() {
+ initializeVarsForSmallTablet(true)
useTwoPanels = true
val dp = DeviceProfile(
@@ -121,7 +125,8 @@
windowBounds,
isMultiWindowMode,
transposeLayoutWithOrientation,
- useTwoPanels
+ useTwoPanels,
+ isGestureMode
)
assertThat(dp.isQsbInline).isFalse()
@@ -137,11 +142,12 @@
windowBounds = WindowBounds(x, y, x, y - 100)
`when`(info.isTablet(any())).thenReturn(false)
+ `when`(info.isLargeTablet(any())).thenReturn(false)
scalableInvariantDeviceProfile()
}
- private fun initializeVarsForTablet(isLandscape: Boolean = false) {
+ private fun initializeVarsForSmallTablet(isLandscape: Boolean = false) {
val (x, y) = if (isLandscape)
Pair(2560, 1600)
else
@@ -150,6 +156,21 @@
windowBounds = WindowBounds(x, y, x, y - 100)
`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)
+
+ `when`(info.isTablet(any())).thenReturn(true)
+ `when`(info.isLargeTablet(any())).thenReturn(true)
scalableInvariantDeviceProfile()
}