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();
     }