diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 99a337e034..620eb7b0fa 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -152,6 +152,9 @@ + + diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 97c0f38110..cd9bbf70df 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -34,7 +34,6 @@ import android.view.Surface; 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 @@ public class DeviceProfile { // 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 @@ public class DeviceProfile { // 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 @@ public class DeviceProfile { /** 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 @@ public class DeviceProfile { // 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 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { private boolean mIsMultiWindowMode = false; private Boolean mTransposeLayoutWithOrientation; + private Boolean mIsGestureMode; public Builder(Context context, InvariantDeviceProfile inv, Info info) { mContext = context; @@ -1294,6 +1307,11 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { 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 dfe4bb0204..886c6573d7 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -19,6 +19,7 @@ package com.android.launcher3; 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; @@ -137,6 +138,11 @@ public class InvariantDeviceProfile { */ 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 @@ -188,7 +194,8 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 5f4119241b..f944d3c2e3 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.LAUNCH 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 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { } /** - * 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 e1b9478832..873a5189b2 100644 --- a/src/com/android/launcher3/util/WindowManagerCompat.java +++ b/src/com/android/launcher3/util/WindowManagerCompat.java @@ -45,6 +45,7 @@ import java.util.Set; 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 6c99a21d67..60046a0768 100644 --- a/tests/src/com/android/launcher3/DeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/DeviceProfileTest.kt @@ -39,6 +39,7 @@ class DeviceProfileTest { 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 @@ class DeviceProfileTest { windowBounds, isMultiWindowMode, transposeLayoutWithOrientation, - useTwoPanels + useTwoPanels, + isGestureMode ) assertThat(dp.isQsbInline).isFalse() @@ -67,7 +69,7 @@ class DeviceProfileTest { @Test fun qsbWidth_is_match_parent_for_tablet_portrait() { - initializeVarsForTablet() + initializeVarsForLargeTablet() val dp = DeviceProfile( context, @@ -76,7 +78,8 @@ class DeviceProfileTest { windowBounds, isMultiWindowMode, transposeLayoutWithOrientation, - useTwoPanels + useTwoPanels, + isGestureMode ) assertThat(dp.isQsbInline).isFalse() @@ -84,8 +87,8 @@ class DeviceProfileTest { } @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 @@ class DeviceProfileTest { windowBounds, isMultiWindowMode, transposeLayoutWithOrientation, - useTwoPanels + useTwoPanels, + isGestureMode ) if (dp.hotseatQsbHeight > 0) { @@ -110,8 +114,8 @@ class DeviceProfileTest { * 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 @@ class DeviceProfileTest { windowBounds, isMultiWindowMode, transposeLayoutWithOrientation, - useTwoPanels + useTwoPanels, + isGestureMode ) assertThat(dp.isQsbInline).isFalse() @@ -137,11 +142,12 @@ class DeviceProfileTest { 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 @@ class DeviceProfileTest { 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() }