Add Launcher support for left-right splits on portrait foldables
Bug: 291018646
Test: Manual
Flag: enable_left_right_split_in_portrait
Change-Id: I92af7ffd83770f7e942c3892a12b1611ff5b50f4
Signed-off-by: Winson Chung <winsonc@google.com>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7ca8b82..c5ff6f3 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -105,6 +105,8 @@
public final boolean isMultiWindowMode;
public final boolean isGestureMode;
+ public final boolean isLeftRightSplit;
+
public final int windowX;
public final int windowY;
public final int widthPx;
@@ -691,6 +693,20 @@
overviewGridSideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
splitPlaceholderInset = res.getDimensionPixelSize(R.dimen.split_placeholder_inset);
+ // We need to use the full window bounds for split determination because on near-square
+ // devices, the available bounds (bounds minus insets) may actually be in landscape while
+ // actually portrait
+ int leftRightSplitPortraitResId = Resources.getSystem().getIdentifier(
+ "config_leftRightSplitInPortrait", "bool", "android");
+ boolean allowLeftRightSplitInPortrait =
+ com.android.wm.shell.Flags.enableLeftRightSplitInPortrait()
+ && leftRightSplitPortraitResId > 0
+ && res.getBoolean(leftRightSplitPortraitResId);
+ if (allowLeftRightSplitInPortrait && isTablet) {
+ isLeftRightSplit = !isLandscape;
+ } else {
+ isLeftRightSplit = isLandscape;
+ }
// Calculate all of the remaining variables.
extraSpace = updateAvailableDimensions(res);
@@ -1986,6 +2002,7 @@
writer.println(prefix + "\tisLandscape:" + isLandscape);
writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
writer.println(prefix + "\tisTwoPanels:" + isTwoPanels);
+ writer.println(prefix + "\tisLeftRightSplit:" + isLeftRightSplit);
writer.println(prefix + pxToDpStr("windowX", windowX));
writer.println(prefix + pxToDpStr("windowY", windowY));
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8ba6d2c..b74699a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -758,10 +758,16 @@
*/
public static List<SplitPositionOption> getSplitPositionOptions(
DeviceProfile dp) {
+ int splitIconRes = dp.isLeftRightSplit
+ ? R.drawable.ic_split_horizontal
+ : R.drawable.ic_split_vertical;
+ int stagePosition = dp.isLeftRightSplit
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT
+ : STAGE_POSITION_TOP_OR_LEFT;
return Collections.singletonList(new SplitPositionOption(
- dp.isLandscape ? R.drawable.ic_split_horizontal : R.drawable.ic_split_vertical,
+ splitIconRes,
R.string.recent_task_option_split_screen,
- dp.isLandscape ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT,
+ stagePosition,
STAGE_TYPE_MAIN
));
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 8121245..4cf6471 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -134,7 +134,7 @@
mCenterChannelSize = CENTER_CHANNEL_SCALE * defaultIconSize;
mBigRadius = BIG_RADIUS_SCALE * defaultIconSize;
mSmallRadius = SMALL_RADIUS_SCALE * defaultIconSize;
- mIsLandscape = grid.isLandscape;
+ mIsLandscape = grid.isLeftRightSplit;
// Calculate drawable area position
float leftBound = (canvas.getWidth() / 2f) - (defaultIconSize / 2f);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 158747c..04b6710 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -258,7 +258,7 @@
@Override
public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
- if (deviceProfile.isLandscape && stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ if (deviceProfile.isLeftRightSplit && stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
return -1;
} else {
return 1;
@@ -348,7 +348,7 @@
}
// Set translations
- if (deviceProfile.isLandscape) {
+ if (deviceProfile.isLeftRightSplit) {
if (desiredTaskId == splitBounds.rightBottomTaskId) {
float leftTopTaskPercent = splitBounds.appsStackedVertically
? splitBounds.topTaskPercent
@@ -430,7 +430,7 @@
options.add(new SplitPositionOption(
R.drawable.ic_split_horizontal, R.string.recent_task_option_split_screen,
STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
- } else if (dp.isLandscape) {
+ } else if (dp.isLeftRightSplit) {
options.add(new SplitPositionOption(
R.drawable.ic_split_horizontal, R.string.recent_task_option_split_screen,
STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
@@ -452,7 +452,7 @@
int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight);
out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment);
- if (!dp.isLandscape) {
+ if (!dp.isLeftRightSplit) {
// portrait, phone or tablet - spans width of screen, nothing else to do
out.inset(placeholderInset, 0);
@@ -497,7 +497,7 @@
@StagePosition int stagePosition) {
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
float insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f;
- if (!dp.isLandscape) {
+ if (!dp.isLeftRightSplit) {
out.setX(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2);
out.setY((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
@@ -566,7 +566,7 @@
int screenWidth = dp.widthPx;
out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize);
out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight);
- if (!dp.isLandscape) {
+ if (!dp.isLeftRightSplit) {
// Portrait - the window bounds are always top and bottom half
return;
}
@@ -607,13 +607,13 @@
float scaledDividerHeight = dividerHeight * scale;
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- if (dp.isLandscape) {
+ if (dp.isLeftRightSplit) {
outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
} else {
outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
}
} else {
- if (dp.isLandscape) {
+ if (dp.isLeftRightSplit) {
outRect.left += Math.round(outRect.width()
* (topLeftTaskPercent + dividerBarPercent));
} else {
@@ -637,7 +637,7 @@
int secondarySnapshotWidth;
float taskPercent = splitBoundsConfig.appsStackedVertically ?
splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
- if (dp.isLandscape) {
+ if (dp.isLeftRightSplit) {
int scaledDividerBar = Math.round(parentWidth * dividerScale);
primarySnapshotHeight = totalThumbnailHeight;
primarySnapshotWidth = Math.round(parentWidth * taskPercent);
@@ -742,7 +742,7 @@
secondaryIconParams.gravity = TOP | START;
secondaryIconParams.topMargin = primaryIconParams.topMargin;
secondaryIconParams.setMarginStart(primaryIconParams.getMarginStart());
- if (deviceProfile.isLandscape) {
+ if (deviceProfile.isLeftRightSplit) {
if (isRtl) {
primaryIconView.setTranslationX(-primarySnapshotWidth);
} else {
@@ -756,7 +756,7 @@
secondaryIconView.setTranslationY(
primarySnapshotHeight + (deviceProfile.isTablet ? 0 : dividerThickness));
}
- } else if (deviceProfile.isLandscape) {
+ } else if (deviceProfile.isLeftRightSplit) {
// We calculate the "midpoint" of the thumbnail area, and place the icons there.
// This is the place where the thumbnail area splits by default, in a near-50/50 split.
// It is usually not exactly 50/50, due to insets/screen cutouts.
@@ -826,7 +826,7 @@
if (!deviceProfile.isTablet) {
throw new IllegalStateException("Default position available only for large screens");
}
- if (deviceProfile.isLandscape) {
+ if (deviceProfile.isLeftRightSplit) {
return STAGE_POSITION_BOTTOM_OR_RIGHT;
} else {
return STAGE_POSITION_TOP_OR_LEFT;
@@ -836,7 +836,7 @@
@Override
public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
FloatProperty secondary, DeviceProfile deviceProfile) {
- if (deviceProfile.isLandscape) { // or seascape
+ if (deviceProfile.isLeftRightSplit) { // or seascape
return new Pair<>(primary, secondary);
} else {
return new Pair<>(secondary, primary);
@@ -846,7 +846,7 @@
@Override
public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
@StagePosition int stagePosition, DeviceProfile dp) {
- if (dp.isLandscape) {
+ if (dp.isLeftRightSplit) {
float currentTranslationX = floatingTask.getTranslationX();
return stagePosition == STAGE_POSITION_TOP_OR_LEFT
? currentTranslationX - onScreenRect.width()
@@ -860,7 +860,7 @@
@Override
public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
DeviceProfile dp) {
- if (dp.isLandscape) {
+ if (dp.isLeftRightSplit) {
floatingTask.setTranslationX(translation);
} else {
floatingTask.setTranslationY(translation);
@@ -870,7 +870,7 @@
@Override
public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
- return dp.isLandscape
+ return dp.isLeftRightSplit
? floatingTask.getTranslationX()
: floatingTask.getTranslationY();
}