Add support for left-right splits on portrait devices

- Add a config to transpose the split such that you can split left/right
  while in portrait.  This is mostly useful for portrait based foldable
  devices.
- Refactors the divider, split layout, and drag and drop to be based on
  this new config vs the configuration orientation

Bug: 291018646
Test: Manual
Flag: enable_left_right_split_in_portrait
Change-Id: I8b3b81f2510457c31ababb2db1b5e4031d6dc17c
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 4d2d960..4511f3b 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -50,3 +50,10 @@
     bug: "290220798"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "enable_left_right_split_in_portrait"
+    namespace: "multitasking"
+    description: "Enables left/right split in portrait"
+    bug: "291018646"
+}
diff --git a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
deleted file mode 100644
index d732b01..0000000
--- a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-
-<com.android.wm.shell.legacysplitscreen.DividerView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent">
-
-    <View
-        style="@style/DockedDividerBackground"
-        android:id="@+id/docked_divider_background"
-        android:background="@color/split_divider_background"/>
-
-    <com.android.wm.shell.legacysplitscreen.MinimizedDockShadow
-        style="@style/DockedDividerMinimizedShadow"
-        android:id="@+id/minimized_dock_shadow"
-        android:alpha="0"/>
-
-    <com.android.wm.shell.common.split.DividerHandleView
-        style="@style/DockedDividerHandle"
-        android:id="@+id/docked_divider_handle"
-        android:contentDescription="@string/accessibility_divider"
-        android:background="@null"/>
-
-</com.android.wm.shell.legacysplitscreen.DividerView>
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index e3be700..db35c8c 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -24,17 +24,16 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <View
-            style="@style/DockedDividerBackground"
-            android:id="@+id/docked_divider_background"/>
-
         <com.android.wm.shell.common.split.DividerHandleView
-            style="@style/DockedDividerHandle"
             android:id="@+id/docked_divider_handle"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:layout_gravity="center"
             android:contentDescription="@string/accessibility_divider"
             android:background="@null"/>
 
         <com.android.wm.shell.common.split.DividerRoundedCorner
+            android:id="@+id/docked_divider_rounded_corner"
             android:layout_width="match_parent"
             android:layout_height="match_parent"/>
 
diff --git a/libs/WindowManager/Shell/res/values-land/dimens.xml b/libs/WindowManager/Shell/res/values-land/dimens.xml
index a95323f..1b96fa2 100644
--- a/libs/WindowManager/Shell/res/values-land/dimens.xml
+++ b/libs/WindowManager/Shell/res/values-land/dimens.xml
@@ -16,13 +16,6 @@
 */
 -->
 <resources>
-    <!-- Divider handle size for legacy split screen -->
-    <dimen name="docked_divider_handle_width">2dp</dimen>
-    <dimen name="docked_divider_handle_height">16dp</dimen>
-    <!-- Divider handle size for split screen -->
-    <dimen name="split_divider_handle_width">3dp</dimen>
-    <dimen name="split_divider_handle_height">72dp</dimen>
-
     <!-- Padding between status bar and bubbles when displayed in expanded state, smaller
      value in landscape since we have limited vertical space-->
     <dimen name="bubble_padding_top">4dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
deleted file mode 100644
index e89f65b..0000000
--- a/libs/WindowManager/Shell/res/values-land/styles.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="DockedDividerBackground">
-        <item name="android:layout_width">@dimen/split_divider_bar_width</item>
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_gravity">center_horizontal</item>
-        <item name="android:background">@color/split_divider_background</item>
-    </style>
-
-    <style name="DockedDividerHandle">
-        <item name="android:layout_gravity">center</item>
-        <item name="android:layout_width">48dp</item>
-        <item name="android:layout_height">96dp</item>
-    </style>
-
-    <style name="DockedDividerMinimizedShadow">
-        <item name="android:layout_width">8dp</item>
-        <item name="android:layout_height">match_parent</item>
-    </style>
-</resources>
-
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f20d44d..8f9de61 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -96,6 +96,9 @@
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
     <!-- Divider handle size for split screen -->
+    <dimen name="split_divider_handle_region_width">96dp</dimen>
+    <dimen name="split_divider_handle_region_height">48dp</dimen>
+
     <dimen name="split_divider_handle_width">72dp</dimen>
     <dimen name="split_divider_handle_height">3dp</dimen>
 
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 468cfd5..08c2a02 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -60,20 +60,9 @@
 
     <style name="DockedDividerBackground">
         <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">@dimen/split_divider_bar_width</item>
-        <item name="android:layout_gravity">center_vertical</item>
-        <item name="android:background">@color/split_divider_background</item>
-    </style>
-
-    <style name="DockedDividerMinimizedShadow">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">8dp</item>
-    </style>
-
-    <style name="DockedDividerHandle">
+        <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
-        <item name="android:layout_width">96dp</item>
-        <item name="android:layout_height">48dp</item>
+        <item name="android:background">@color/split_divider_background</item>
     </style>
 
     <style name="TvPipEduText">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index ec26800..999da24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -68,24 +68,33 @@
             };
 
     private final Paint mPaint = new Paint();
-    private final int mWidth;
-    private final int mHeight;
-    private final int mTouchingWidth;
-    private final int mTouchingHeight;
+    private int mWidth;
+    private int mHeight;
+    private int mTouchingWidth;
+    private int mTouchingHeight;
     private int mCurrentWidth;
     private int mCurrentHeight;
     private AnimatorSet mAnimator;
     private boolean mTouching;
     private boolean mHovering;
-    private final int mHoveringWidth;
-    private final int mHoveringHeight;
+    private int mHoveringWidth;
+    private int mHoveringHeight;
+    private boolean mIsLeftRightSplit;
 
     public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
         mPaint.setAntiAlias(true);
-        mWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_width);
-        mHeight = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_height);
+        updateDimens();
+    }
+
+    private void updateDimens() {
+        mWidth = getResources().getDimensionPixelSize(mIsLeftRightSplit
+                ? R.dimen.split_divider_handle_height
+                : R.dimen.split_divider_handle_width);
+        mHeight = getResources().getDimensionPixelSize(mIsLeftRightSplit
+                ? R.dimen.split_divider_handle_width
+                : R.dimen.split_divider_handle_height);
         mCurrentWidth = mWidth;
         mCurrentHeight = mHeight;
         mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth;
@@ -94,6 +103,11 @@
         mHoveringHeight = mHeight > mWidth ? ((int) (mHeight * 1.5f)) : mHeight;
     }
 
+    void setIsLeftRightSplit(boolean isLeftRightSplit) {
+        mIsLeftRightSplit = isLeftRightSplit;
+        updateDimens();
+    }
+
     /** Sets touching state for this handle view. */
     public void setTouching(boolean touching, boolean animate) {
         if (touching == mTouching) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
index 364bb65..834c15d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
 import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
 import static android.view.RoundedCorner.POSITION_TOP_LEFT;
@@ -47,6 +46,7 @@
     private InvertedRoundedCornerDrawInfo mTopRightCorner;
     private InvertedRoundedCornerDrawInfo mBottomLeftCorner;
     private InvertedRoundedCornerDrawInfo mBottomRightCorner;
+    private boolean mIsLeftRightSplit;
 
     public DividerRoundedCorner(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -98,8 +98,8 @@
         return false;
     }
 
-    private boolean isLandscape() {
-        return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+    void setIsLeftRightSplit(boolean isLeftRightSplit) {
+        mIsLeftRightSplit = isLeftRightSplit;
     }
 
     /**
@@ -134,7 +134,7 @@
         }
 
         private void calculateStartPos(Point outPos) {
-            if (isLandscape()) {
+            if (mIsLeftRightSplit) {
                 // Place left corner at the right side of the divider bar.
                 outPos.x = isLeftCorner()
                         ? getWidth() / 2 + mDividerWidth / 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 0b0c693..0f0fbd9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
@@ -27,6 +26,8 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.DeviceConfig;
@@ -65,12 +66,15 @@
     public static final long TOUCH_ANIMATION_DURATION = 150;
     public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
 
+    private final Paint mPaint = new Paint();
+    private final Rect mBackgroundRect = new Rect();
     private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
     private SplitLayout mSplitLayout;
     private SplitWindowManager mSplitWindowManager;
     private SurfaceControlViewHost mViewHost;
     private DividerHandleView mHandle;
+    private DividerRoundedCorner mCorners;
     private View mBackground;
     private int mTouchElevation;
 
@@ -81,6 +85,8 @@
     private boolean mInteractive;
     private boolean mSetTouchRegion = true;
     private int mLastDraggingPosition;
+    private int mHandleRegionWidth;
+    private int mHandleRegionHeight;
 
     /**
      * Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
@@ -123,7 +129,7 @@
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
             final DividerSnapAlgorithm snapAlgorithm = mSplitLayout.mDividerSnapAlgorithm;
-            if (isLandscape()) {
+            if (mSplitLayout.isLeftRightSplit()) {
                 info.addAction(new AccessibilityAction(R.id.action_move_tl_full,
                         mContext.getString(R.string.accessibility_action_divider_left_full)));
                 if (snapAlgorithm.isFirstSplitTargetAvailable()) {
@@ -215,6 +221,17 @@
         mViewHost = viewHost;
         layout.getDividerBounds(mDividerBounds);
         onInsetsChanged(insetsState, false /* animate */);
+
+        final boolean isLeftRightSplit = mSplitLayout.isLeftRightSplit();
+        mHandle.setIsLeftRightSplit(isLeftRightSplit);
+        mCorners.setIsLeftRightSplit(isLeftRightSplit);
+
+        mHandleRegionWidth = getResources().getDimensionPixelSize(isLeftRightSplit
+                ? R.dimen.split_divider_handle_region_height
+                : R.dimen.split_divider_handle_region_width);
+        mHandleRegionHeight = getResources().getDimensionPixelSize(isLeftRightSplit
+                ? R.dimen.split_divider_handle_region_width
+                : R.dimen.split_divider_handle_region_height);
     }
 
     void onInsetsChanged(InsetsState insetsState, boolean animate) {
@@ -255,30 +272,47 @@
         super.onFinishInflate();
         mDividerBar = findViewById(R.id.divider_bar);
         mHandle = findViewById(R.id.docked_divider_handle);
-        mBackground = findViewById(R.id.docked_divider_background);
+        mCorners = findViewById(R.id.docked_divider_rounded_corner);
         mTouchElevation = getResources().getDimensionPixelSize(
                 R.dimen.docked_stack_divider_lift_elevation);
         mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
         mInteractive = true;
         setOnTouchListener(this);
         mHandle.setAccessibilityDelegate(mHandleDelegate);
+        setWillNotDraw(false);
+        mPaint.setColor(getResources().getColor(R.color.split_divider_background, null));
+        mPaint.setAntiAlias(true);
+        mPaint.setStyle(Paint.Style.FILL);
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         if (mSetTouchRegion) {
-            mTempRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(),
-                    mHandle.getBottom());
+            int startX = (mDividerBounds.width() - mHandleRegionWidth) / 2;
+            int startY = (mDividerBounds.height() - mHandleRegionHeight) / 2;
+            mTempRect.set(startX, startY, startX + mHandleRegionWidth,
+                    startY + mHandleRegionHeight);
             mSplitWindowManager.setTouchRegion(mTempRect);
             mSetTouchRegion = false;
         }
+
+        if (changed) {
+            boolean isHorizontalSplit = mSplitLayout.isLeftRightSplit();
+            int dividerSize = getResources().getDimensionPixelSize(R.dimen.split_divider_bar_width);
+            left = isHorizontalSplit ? (getWidth() - dividerSize) / 2 : 0;
+            top = isHorizontalSplit ? 0 : (getHeight() - dividerSize) / 2;
+            right = isHorizontalSplit ? left + dividerSize : getWidth();
+            bottom = isHorizontalSplit ? getHeight() : top + dividerSize;
+            mBackgroundRect.set(left, top, right, bottom);
+        }
     }
 
     @Override
     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
         return PointerIcon.getSystemIcon(getContext(),
-                isLandscape() ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW);
+                mSplitLayout.isLeftRightSplit() ? TYPE_HORIZONTAL_DOUBLE_ARROW
+                        : TYPE_VERTICAL_DOUBLE_ARROW);
     }
 
     @Override
@@ -295,8 +329,8 @@
         // moving divider bar and calculating dragging velocity.
         event.setLocation(event.getRawX(), event.getRawY());
         final int action = event.getAction() & MotionEvent.ACTION_MASK;
-        final boolean isLandscape = isLandscape();
-        final int touchPos = (int) (isLandscape ? event.getX() : event.getY());
+        final boolean isLeftRightSplit = mSplitLayout.isLeftRightSplit();
+        final int touchPos = (int) (isLeftRightSplit ? event.getX() : event.getY());
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mVelocityTracker = VelocityTracker.obtain();
@@ -328,7 +362,7 @@
 
                 mVelocityTracker.addMovement(event);
                 mVelocityTracker.computeCurrentVelocity(1000 /* units */);
-                final float velocity = isLandscape
+                final float velocity = isLeftRightSplit
                         ? mVelocityTracker.getXVelocity()
                         : mVelocityTracker.getYVelocity();
                 final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
@@ -410,6 +444,11 @@
                 .start();
     }
 
+    @Override
+    protected void onDraw(@NonNull Canvas canvas) {
+        canvas.drawRect(mBackgroundRect, mPaint);
+    }
+
     @VisibleForTesting
     void releaseHovering() {
         mHandle.setHovering(false, true);
@@ -446,10 +485,6 @@
         mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE);
     }
 
-    private boolean isLandscape() {
-        return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
-    }
-
     private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
         @Override
         public boolean onDoubleTap(MotionEvent e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 63cdb4f..b699533 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -79,7 +79,7 @@
  * divide position changes.
  */
 public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
-
+    private static final String TAG = "SplitLayout";
     public static final int PARALLAX_NONE = 0;
     public static final int PARALLAX_DISMISSING = 1;
     public static final int PARALLAX_ALIGN_CENTER = 2;
@@ -121,12 +121,15 @@
     private int mDividerPosition;
     private boolean mInitialized = false;
     private boolean mFreezeDividerWindow = false;
+    private boolean mIsLargeScreen = false;
     private int mOrientation;
     private int mRotation;
     private int mDensity;
     private int mUiMode;
 
     private final boolean mDimNonImeSide;
+    private final boolean mAllowLeftRightSplitInPortrait;
+    private boolean mIsLeftRightSplit;
     private ValueAnimator mDividerFlingAnimator;
 
     public SplitLayout(String windowName, Context context, Configuration configuration,
@@ -138,6 +141,7 @@
         mOrientation = configuration.orientation;
         mRotation = configuration.windowConfiguration.getRotation();
         mDensity = configuration.densityDpi;
+        mIsLargeScreen = configuration.smallestScreenWidthDp >= 600;
         mSplitLayoutHandler = splitLayoutHandler;
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
@@ -147,14 +151,17 @@
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
         mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
 
+        final Resources res = mContext.getResources();
+        mDimNonImeSide = res.getBoolean(R.bool.config_dimNonImeAttachedSide);
+        mAllowLeftRightSplitInPortrait = SplitScreenUtils.allowLeftRightSplitInPortrait(res);
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                configuration);
+
         updateDividerConfig(mContext);
 
         mRootBounds.set(configuration.windowConfiguration.getBounds());
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
         resetDividerPosition();
-
-        mDimNonImeSide = mContext.getResources().getBoolean(R.bool.config_dimNonImeAttachedSide);
-
         updateInvisibleRect();
     }
 
@@ -284,17 +291,17 @@
      * Returns the divider position as a fraction from 0 to 1.
      */
     public float getDividerPositionAsFraction() {
-        return Math.min(1f, Math.max(0f, isLandscape()
+        return Math.min(1f, Math.max(0f, mIsLeftRightSplit
                 ? (float) ((mBounds1.right + mBounds2.left) / 2f) / mBounds2.right
                 : (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom));
     }
 
     private void updateInvisibleRect() {
         mInvisibleBounds.set(mRootBounds.left, mRootBounds.top,
-                isLandscape() ? mRootBounds.right / 2 : mRootBounds.right,
-                isLandscape() ? mRootBounds.bottom : mRootBounds.bottom / 2);
-        mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
-                isLandscape() ? 0 : mRootBounds.bottom);
+                mIsLeftRightSplit ? mRootBounds.right / 2 : mRootBounds.right,
+                mIsLeftRightSplit ? mRootBounds.bottom : mRootBounds.bottom / 2);
+        mInvisibleBounds.offset(mIsLeftRightSplit ? mRootBounds.right : 0,
+                mIsLeftRightSplit ? 0 : mRootBounds.bottom);
     }
 
     /** Applies new configuration, returns {@code false} if there's no effect to the layout. */
@@ -309,6 +316,7 @@
         final int orientation = configuration.orientation;
         final int density = configuration.densityDpi;
         final int uiMode = configuration.uiMode;
+        final boolean wasLeftRightSplit = mIsLeftRightSplit;
 
         if (mOrientation == orientation
                 && mRotation == rotation
@@ -326,9 +334,12 @@
         mRotation = rotation;
         mDensity = density;
         mUiMode = uiMode;
+        mIsLargeScreen = configuration.smallestScreenWidthDp >= 600;
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                configuration);
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
         updateDividerConfig(mContext);
-        initDividerPosition(mTempRect);
+        initDividerPosition(mTempRect, wasLeftRightSplit);
         updateInvisibleRect();
 
         return true;
@@ -347,18 +358,27 @@
         }
 
         // We only need new bounds here, other configuration should be update later.
+        final boolean wasLeftRightSplit = SplitScreenUtils.isLeftRightSplit(
+                mAllowLeftRightSplitInPortrait, mIsLargeScreen,
+                mRootBounds.width() >= mRootBounds.height());
         mTempRect.set(mRootBounds);
         mRootBounds.set(tmpRect);
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                mIsLargeScreen, mRootBounds.width() >= mRootBounds.height());
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
-        initDividerPosition(mTempRect);
+        initDividerPosition(mTempRect, wasLeftRightSplit);
     }
 
-    private void initDividerPosition(Rect oldBounds) {
+    /**
+     * Updates the divider position to the position in the current orientation and bounds using the
+     * snap fraction calculated based on the previous orientation and bounds.
+     */
+    private void initDividerPosition(Rect oldBounds, boolean wasLeftRightSplit) {
         final float snapRatio = (float) mDividerPosition
-                / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
+                / (float) (wasLeftRightSplit ? oldBounds.width() : oldBounds.height());
         // Estimate position by previous ratio.
         final float length =
-                (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
+                (float) (mIsLeftRightSplit ? mRootBounds.width() : mRootBounds.height());
         final int estimatePosition = (int) (length * snapRatio);
         // Init divider position by estimated position using current bounds snap algorithm.
         mDividerPosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
@@ -376,8 +396,7 @@
         dividerBounds.set(mRootBounds);
         bounds1.set(mRootBounds);
         bounds2.set(mRootBounds);
-        final boolean isLandscape = isLandscape(mRootBounds);
-        if (isLandscape) {
+        if (mIsLeftRightSplit) {
             position += mRootBounds.left;
             dividerBounds.left = position - mDividerInsets;
             dividerBounds.right = dividerBounds.left + mDividerWindowWidth;
@@ -393,7 +412,7 @@
         DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
         DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
         if (setEffectBounds) {
-            mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
+            mSurfaceEffectPolicy.applyDividerPosition(position, mIsLeftRightSplit);
         }
     }
 
@@ -563,13 +582,12 @@
     }
 
     private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
-        final boolean isLandscape = isLandscape(rootBounds);
         final Rect insets = getDisplayStableInsets(context);
 
         // Make split axis insets value same as the larger one to avoid bounds1 and bounds2
         // have difference for avoiding size-compat mode when switching unresizable apps in
         // landscape while they are letterboxed.
-        if (!isLandscape) {
+        if (!mIsLeftRightSplit) {
             final int largerInsets = Math.max(insets.top, insets.bottom);
             insets.set(insets.left, largerInsets, insets.right, largerInsets);
         }
@@ -579,9 +597,9 @@
                 rootBounds.width(),
                 rootBounds.height(),
                 mDividerSize,
-                !isLandscape,
+                !mIsLeftRightSplit,
                 insets,
-                isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
+                mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
     }
 
     /** Fling divider from current position to end or start position then exit */
@@ -643,13 +661,12 @@
     /** Switch both surface position with animation. */
     public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
             SurfaceControl leash2, Consumer<Rect> finishCallback) {
-        final boolean isLandscape = isLandscape();
         final Rect insets = getDisplayStableInsets(mContext);
-        insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top,
-                isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom);
+        insets.set(mIsLeftRightSplit ? insets.left : 0, mIsLeftRightSplit ? 0 : insets.top,
+                mIsLeftRightSplit ? insets.right : 0, mIsLeftRightSplit ? 0 : insets.bottom);
 
         final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
-                isLandscape ? mBounds2.width() : mBounds2.height()).position;
+                mIsLeftRightSplit ? mBounds2.width() : mBounds2.height()).position;
         final Rect distBounds1 = new Rect();
         final Rect distBounds2 = new Rect();
         final Rect distDividerBounds = new Rect();
@@ -740,15 +757,12 @@
                         .toRect();
     }
 
-    private static boolean isLandscape(Rect bounds) {
-        return bounds.width() > bounds.height();
-    }
-
     /**
-     * Return if this layout is landscape.
+     * @return {@code true} if we should create a left-right split, {@code false} if we should
+     * create a top-bottom split.
      */
-    public boolean isLandscape() {
-        return isLandscape(mRootBounds);
+    public boolean isLeftRightSplit() {
+        return mIsLeftRightSplit;
     }
 
     /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
@@ -850,9 +864,13 @@
 
     /** Dumps the current split bounds recorded in this layout. */
     public void dump(@NonNull PrintWriter pw, String prefix) {
-        pw.println(prefix + "bounds1=" + mBounds1.toShortString());
-        pw.println(prefix + "dividerBounds=" + mDividerBounds.toShortString());
-        pw.println(prefix + "bounds2=" + mBounds2.toShortString());
+        final String innerPrefix = prefix + "\t";
+        pw.println(prefix + TAG + ":");
+        pw.println(innerPrefix + "mAllowLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait);
+        pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit);
+        pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString());
+        pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString());
+        pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString());
     }
 
     /** Handles layout change event. */
@@ -937,32 +955,32 @@
          * Applies a parallax to the task to hint dismissing progress.
          *
          * @param position    the split position to apply dismissing parallax effect
-         * @param isLandscape indicates whether it's splitting horizontally or vertically
+         * @param isLeftRightSplit indicates whether it's splitting horizontally or vertically
          */
-        void applyDividerPosition(int position, boolean isLandscape) {
+        void applyDividerPosition(int position, boolean isLeftRightSplit) {
             mDismissingSide = DOCKED_INVALID;
             mParallaxOffset.set(0, 0);
             mDismissingDimValue = 0;
 
             int totalDismissingDistance = 0;
             if (position < mDividerSnapAlgorithm.getFirstSplitTarget().position) {
-                mDismissingSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                mDismissingSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
                 totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
                         - mDividerSnapAlgorithm.getFirstSplitTarget().position;
             } else if (position > mDividerSnapAlgorithm.getLastSplitTarget().position) {
-                mDismissingSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                mDismissingSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
                 totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
                         - mDividerSnapAlgorithm.getDismissEndTarget().position;
             }
 
-            final boolean topLeftShrink = isLandscape
+            final boolean topLeftShrink = isLeftRightSplit
                     ? position < mWinBounds1.right : position < mWinBounds1.bottom;
             if (topLeftShrink) {
-                mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
                 mContentBounds.set(mWinBounds1);
                 mSurfaceBounds.set(mBounds1);
             } else {
-                mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
                 mContentBounds.set(mWinBounds2);
                 mSurfaceBounds.set(mBounds2);
             }
@@ -973,7 +991,7 @@
                 mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
                 if (mParallaxType == PARALLAX_DISMISSING) {
                     fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
-                    if (isLandscape) {
+                    if (isLeftRightSplit) {
                         mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
                     } else {
                         mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
@@ -982,7 +1000,7 @@
             }
 
             if (mParallaxType == PARALLAX_ALIGN_CENTER) {
-                if (isLandscape) {
+                if (isLeftRightSplit) {
                     mParallaxOffset.x =
                             (mSurfaceBounds.width() - mContentBounds.width()) / 2;
                 } else {
@@ -1129,7 +1147,7 @@
             // Calculate target bounds offset for IME
             mLastYOffset = mYOffsetForIme;
             final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
-                    && !isFloating && !isLandscape(mRootBounds) && mImeShown;
+                    && !isFloating && !mIsLeftRightSplit && mImeShown;
             mTargetYOffset = needOffset ? getTargetYOffset() : 0;
 
             if (mTargetYOffset != mLastYOffset) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index d7ea1c0..0693543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.common.split;
 
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -25,9 +27,14 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 
 /** Helper utility class for split screen components to use. */
@@ -94,4 +101,38 @@
     public static String splitFailureMessage(String caller, String reason) {
         return "(" + caller + ") Splitscreen aborted: " + reason;
     }
+
+    /**
+     * Returns whether left/right split is allowed in portrait.
+     */
+    public static boolean allowLeftRightSplitInPortrait(Resources res) {
+        return Flags.enableLeftRightSplitInPortrait() && res.getBoolean(
+                com.android.internal.R.bool.config_leftRightSplitInPortrait);
+    }
+
+    /**
+     * Returns whether left/right split is supported in the given configuration.
+     */
+    public static boolean isLeftRightSplit(boolean allowLeftRightSplitInPortrait,
+            Configuration config) {
+        // Compare the max bounds sizes as on near-square devices, the insets may result in a
+        // configuration in the other orientation
+        final boolean isLargeScreen = config.smallestScreenWidthDp >= 600;
+        final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+        final boolean isLandscape = maxBounds.width() >= maxBounds.height();
+        return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen, isLandscape);
+    }
+
+    /**
+     * Returns whether left/right split is supported in the given configuration state. This method
+     * is useful for cases where we need to calculate this given last saved state.
+     */
+    public static boolean isLeftRightSplit(boolean allowLeftRightSplitInPortrait,
+            boolean isLargeScreen, boolean isLandscape) {
+        if (allowLeftRightSplitInPortrait && isLargeScreen) {
+            return !isLandscape;
+        } else {
+            return isLandscape;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 0bf8ec3..fdfb6f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -94,6 +94,7 @@
     private ShellExecutor mMainExecutor;
     private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();
 
+    // Map of displayId -> per-display info
     private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
 
     /**
@@ -362,7 +363,7 @@
      */
     private boolean isReadyToHandleDrag() {
         for (int i = 0; i < mDisplayDropTargets.size(); i++) {
-            if (mDisplayDropTargets.valueAt(i).mHasDrawn) {
+            if (mDisplayDropTargets.valueAt(i).hasDrawn) {
                 return true;
             }
         }
@@ -398,8 +399,13 @@
      * Dumps information about this controller.
      */
     public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
-        pw.println(prefix + " listeners=" + mListeners.size());
+        pw.println(innerPrefix + "listeners=" + mListeners.size());
+        pw.println(innerPrefix + "Per display:");
+        for (int i = 0; i < mDisplayDropTargets.size(); i++) {
+            mDisplayDropTargets.valueAt(i).dump(pw, innerPrefix);
+        }
     }
     
     /**
@@ -440,7 +446,7 @@
         final FrameLayout rootView;
         final DragLayout dragLayout;
         // Tracks whether the window has fully drawn since it was last made visible
-        boolean mHasDrawn;
+        boolean hasDrawn;
 
         boolean isHandlingDrag;
         // A count of the number of active drags in progress to ensure that we only hide the window
@@ -464,17 +470,29 @@
             rootView.setVisibility(visibility);
             if (visibility == View.VISIBLE) {
                 rootView.requestApplyInsets();
-                if (!mHasDrawn && rootView.getViewRootImpl() != null) {
+                if (!hasDrawn && rootView.getViewRootImpl() != null) {
                     rootView.getViewRootImpl().registerRtFrameCallback(this);
                 }
             } else {
-                mHasDrawn = false;
+                hasDrawn = false;
             }
         }
 
         @Override
         public void onFrameDraw(long frame) {
-            mHasDrawn = true;
+            hasDrawn = true;
+        }
+
+        /**
+         * Dumps information about this display's shell drop target.
+         */
+        public void dump(@NonNull PrintWriter pw, String prefix) {
+            final String innerPrefix = prefix + "  ";
+            pw.println(innerPrefix + "displayId=" + displayId);
+            pw.println(innerPrefix + "hasDrawn=" + hasDrawn);
+            pw.println(innerPrefix + "isHandlingDrag=" + isHandlingDrag);
+            pw.println(innerPrefix + "activeDragCount=" + activeDragCount);
+            dragLayout.dump(pw, innerPrefix);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index e70768b..162ce19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -138,7 +138,7 @@
         final Rect displayRegion = new Rect(l, t, l + iw, t + ih);
         final Rect fullscreenDrawRegion = new Rect(displayRegion);
         final Rect fullscreenHitRegion = new Rect(displayRegion);
-        final boolean inLandscape = mSession.displayLayout.isLandscape();
+        final boolean isLeftRightSplit = mSplitScreen != null && mSplitScreen.isLeftRightSplit();
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
         final float dividerWidth = mContext.getResources().getDimensionPixelSize(
                 R.dimen.split_divider_bar_width);
@@ -155,7 +155,7 @@
             topOrLeftBounds.intersect(displayRegion);
             bottomOrRightBounds.intersect(displayRegion);
 
-            if (inLandscape) {
+            if (isLeftRightSplit) {
                 final Rect leftHitRegion = new Rect();
                 final Rect rightHitRegion = new Rect();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 205a455..445ba89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -47,14 +48,18 @@
 import android.view.WindowInsets.Type;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -74,6 +79,11 @@
     private final StatusBarManager mStatusBarManager;
     private final Configuration mLastConfiguration = new Configuration();
 
+    // Whether this device supports left/right split in portrait
+    private final boolean mAllowLeftRightSplitInPortrait;
+    // Whether the device is currently in left/right split mode
+    private boolean mIsLeftRightSplit;
+
     private DragAndDropPolicy.Target mCurrentTarget = null;
     private DropZoneView mDropZoneView1;
     private DropZoneView mDropZoneView2;
@@ -106,17 +116,18 @@
         setLayoutDirection(LAYOUT_DIRECTION_LTR);
         mDropZoneView1 = new DropZoneView(context);
         mDropZoneView2 = new DropZoneView(context);
-        addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT,
-                MATCH_PARENT));
-        addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT,
-                MATCH_PARENT));
+        addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         ((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
         ((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
-        int orientation = getResources().getConfiguration().orientation;
-        setOrientation(orientation == Configuration.ORIENTATION_LANDSCAPE
-                ? LinearLayout.HORIZONTAL
-                : LinearLayout.VERTICAL);
-        updateContainerMargins(getResources().getConfiguration().orientation);
+        // We don't use the configuration orientation here to determine landscape because
+        // near-square devices may report the same orietation with insets taken into account
+        mAllowLeftRightSplitInPortrait = SplitScreenUtils.allowLeftRightSplitInPortrait(
+                context.getResources());
+        mIsLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                getResources().getConfiguration());
+        setOrientation(mIsLeftRightSplit ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+        updateContainerMargins(mIsLeftRightSplit);
     }
 
     @Override
@@ -124,11 +135,12 @@
         mInsets = insets.getInsets(Type.tappableElement() | Type.displayCutout());
         recomputeDropTargets();
 
-        final int orientation = getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+        boolean isLeftRightSplit = mSplitScreenController != null
+                && mSplitScreenController.isLeftRightSplit();
+        if (isLeftRightSplit) {
             mDropZoneView1.setBottomInset(mInsets.bottom);
             mDropZoneView2.setBottomInset(mInsets.bottom);
-        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+        } else {
             mDropZoneView1.setBottomInset(0);
             mDropZoneView2.setBottomInset(mInsets.bottom);
         }
@@ -136,14 +148,12 @@
     }
 
     public void onConfigChanged(Configuration newConfig) {
-        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
-                && getOrientation() != HORIZONTAL) {
-            setOrientation(LinearLayout.HORIZONTAL);
-            updateContainerMargins(newConfig.orientation);
-        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                && getOrientation() != VERTICAL) {
-            setOrientation(LinearLayout.VERTICAL);
-            updateContainerMargins(newConfig.orientation);
+        boolean isLeftRightSplit = SplitScreenUtils.isLeftRightSplit(mAllowLeftRightSplitInPortrait,
+                newConfig);
+        if (isLeftRightSplit != mIsLeftRightSplit) {
+            mIsLeftRightSplit = isLeftRightSplit;
+            setOrientation(mIsLeftRightSplit ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+            updateContainerMargins(mIsLeftRightSplit);
         }
 
         final int diff = newConfig.diff(mLastConfiguration);
@@ -162,14 +172,14 @@
         mDropZoneView2.setContainerMargin(0, 0, 0, 0);
     }
 
-    private void updateContainerMargins(int orientation) {
+    private void updateContainerMargins(boolean isLeftRightSplit) {
         final float halfMargin = mDisplayMargin / 2f;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+        if (isLeftRightSplit) {
             mDropZoneView1.setContainerMargin(
                     mDisplayMargin, mDisplayMargin, halfMargin, mDisplayMargin);
             mDropZoneView2.setContainerMargin(
                     halfMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
-        } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+        } else {
             mDropZoneView1.setContainerMargin(
                     mDisplayMargin, mDisplayMargin, mDisplayMargin, halfMargin);
             mDropZoneView2.setContainerMargin(
@@ -257,23 +267,21 @@
      * @param bounds2 bounds to apply to the second dropzone view, null if split in half.
      */
     private void updateDropZoneSizes(Rect bounds1, Rect bounds2) {
-        final int orientation = getResources().getConfiguration().orientation;
-        final boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;
         final int halfDivider = mDividerSize / 2;
         final LinearLayout.LayoutParams dropZoneView1 =
                 (LayoutParams) mDropZoneView1.getLayoutParams();
         final LinearLayout.LayoutParams dropZoneView2 =
                 (LayoutParams) mDropZoneView2.getLayoutParams();
-        if (isPortrait) {
-            dropZoneView1.width = MATCH_PARENT;
-            dropZoneView2.width = MATCH_PARENT;
-            dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
-            dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
-        } else {
+        if (mIsLeftRightSplit) {
             dropZoneView1.width = bounds1 != null ? bounds1.width() + halfDivider : MATCH_PARENT;
             dropZoneView2.width = bounds2 != null ? bounds2.width() + halfDivider : MATCH_PARENT;
             dropZoneView1.height = MATCH_PARENT;
             dropZoneView2.height = MATCH_PARENT;
+        } else {
+            dropZoneView1.width = MATCH_PARENT;
+            dropZoneView2.width = MATCH_PARENT;
+            dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
+            dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
         }
         dropZoneView1.weight = bounds1 != null ? 0 : 1;
         dropZoneView2.weight = bounds2 != null ? 0 : 1;
@@ -371,7 +379,7 @@
         // Reset the state if we previously force-ignore the bottom margin
         mDropZoneView1.setForceIgnoreBottomMargin(false);
         mDropZoneView2.setForceIgnoreBottomMargin(false);
-        updateContainerMargins(getResources().getConfiguration().orientation);
+        updateContainerMargins(mIsLeftRightSplit);
         mCurrentTarget = null;
     }
 
@@ -481,4 +489,19 @@
         final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
         return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
     }
+
+    /**
+     * Dumps information about this drag layout.
+     */
+    public void dump(@NonNull PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + "DragLayout:");
+        pw.println(innerPrefix + "mIsLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait);
+        pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit);
+        pw.println(innerPrefix + "mDisplayMargin=" + mDisplayMargin);
+        pw.println(innerPrefix + "mDividerSize=" + mDividerSize);
+        pw.println(innerPrefix + "mIsShowing=" + mIsShowing);
+        pw.println(innerPrefix + "mHasDropped=" + mHasDropped);
+        pw.println(innerPrefix + "mCurrentTarget=" + mCurrentTarget);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index 478b6a9..353d702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -18,31 +18,17 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.ClipDescription.EXTRA_PENDING_INTENT;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
-import static android.content.Intent.EXTRA_USER;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
 import android.app.WindowConfiguration;
 import android.content.ClipData;
-import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.net.Uri;
-import android.os.UserHandle;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 
 import com.android.wm.shell.common.DisplayLayout;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 664d4491..37b24e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -366,6 +366,14 @@
         return mStageCoordinator.getStageOfTask(taskId);
     }
 
+    /**
+     * @return {@code true} if we should create a left-right split, {@code false} if we should
+     * create a top-bottom split.
+     */
+    public boolean isLeftRightSplit() {
+        return mStageCoordinator.isLeftRightSplit();
+    }
+
     /** Check split is foreground and task is under split or not by taskId. */
     public boolean isTaskInSplitScreenForeground(int taskId) {
         return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 7a4834c..36e0eb4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1301,7 +1301,7 @@
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
         mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                 getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                mSplitLayout.isLandscape());
+                mSplitLayout.isLeftRightSplit());
     }
 
     void setSideStagePosition(@SplitPosition int sideStagePosition,
@@ -1659,7 +1659,7 @@
             mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
                     getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                     getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                    mSplitLayout.isLandscape());
+                    mSplitLayout.isLeftRightSplit());
         }
     }
 
@@ -1749,10 +1749,10 @@
         }
         if (stage == STAGE_TYPE_MAIN) {
             mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
-                    mSplitLayout.isLandscape());
+                    mSplitLayout.isLeftRightSplit());
         } else {
             mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                    mSplitLayout.isLandscape());
+                    mSplitLayout.isLeftRightSplit());
         }
         if (present) {
             updateRecentTasksSplitPair();
@@ -2113,7 +2113,7 @@
                 mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
                         getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                         getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                        mSplitLayout.isLandscape());
+                        mSplitLayout.isLeftRightSplit());
             }
         }
     }
@@ -2205,8 +2205,12 @@
         mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
     }
 
-    private boolean isLandscape() {
-        return mSplitLayout.isLandscape();
+    /**
+     * @return {@code true} if we should create a left-right split, {@code false} if we should
+     * create a top-bottom split.
+     */
+    boolean isLeftRightSplit() {
+        return mSplitLayout.isLeftRightSplit();
     }
 
     /**
@@ -3177,6 +3181,7 @@
         pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible);
         pw.println(innerPrefix + "isSplitActive=" + isSplitActive());
         pw.println(innerPrefix + "isSplitVisible=" + isSplitScreenVisible());
+        pw.println(innerPrefix + "isLeftRightSplit=" + mSplitLayout.isLeftRightSplit());
         pw.println(innerPrefix + "MainStage");
         pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
         pw.println(childPrefix + "isActive=" + mMainStage.isActive());
@@ -3188,10 +3193,7 @@
         mSideStage.dump(pw, childPrefix);
         pw.println(innerPrefix + "SideStageListener");
         mSideStageListener.dump(pw, childPrefix);
-        if (mMainStage.isActive()) {
-            pw.println(innerPrefix + "SplitLayout");
-            mSplitLayout.dump(pw, childPrefix);
-        }
+        mSplitLayout.dump(pw, childPrefix);
         if (!mPausingTasks.isEmpty()) {
             pw.println(childPrefix + "mPausingTasks=" + mPausingTasks);
         }
@@ -3243,7 +3245,7 @@
         mLogger.logExit(exitReason,
                 SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */,
                 SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */,
-                mSplitLayout.isLandscape());
+                mSplitLayout.isLeftRightSplit());
     }
 
     /**
@@ -3256,7 +3258,7 @@
                 toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
                 !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED,
                 !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
-                mSplitLayout.isLandscape());
+                mSplitLayout.isLeftRightSplit());
     }
 
     class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 527dc01..1b347e0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -204,6 +204,7 @@
 
     @Test
     public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
+        doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
         setRunningTask(mHomeTask);
         DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
                 mLandscapeDisplayLayout, mActivityClipData);
@@ -219,6 +220,7 @@
 
     @Test
     public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
+        doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
                 mLandscapeDisplayLayout, mActivityClipData);
@@ -239,6 +241,7 @@
 
     @Test
     public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
+        doReturn(false).when(mSplitScreenStarter).isLeftRightSplit();
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mContext, mActivityTaskManager,
                 mPortraitDisplayLayout, mActivityClipData);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenUtilsTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenUtilsTests.java
new file mode 100644
index 0000000..30847d3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenUtilsTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.splitscreen;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.split.SplitScreenUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/** Tests for {@link com.android.wm.shell.common.split.SplitScreenUtils} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SplitScreenUtilsTests extends ShellTestCase {
+
+    @Test
+    public void testIsLeftRightSplit() {
+        Configuration portraitTablet = new Configuration();
+        portraitTablet.smallestScreenWidthDp = 720;
+        portraitTablet.windowConfiguration.setMaxBounds(new Rect(0, 0, 500, 1000));
+        Configuration landscapeTablet = new Configuration();
+        landscapeTablet.smallestScreenWidthDp = 720;
+        landscapeTablet.windowConfiguration.setMaxBounds(new Rect(0, 0, 1000, 500));
+        Configuration portraitPhone = new Configuration();
+        portraitPhone.smallestScreenWidthDp = 420;
+        portraitPhone.windowConfiguration.setMaxBounds(new Rect(0, 0, 500, 1000));
+        Configuration landscapePhone = new Configuration();
+        landscapePhone.smallestScreenWidthDp = 420;
+        landscapePhone.windowConfiguration.setMaxBounds(new Rect(0, 0, 1000, 500));
+
+        // Allow L/R split in portrait = false
+        assertTrue(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                landscapeTablet));
+        assertTrue(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                landscapePhone));
+        assertFalse(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                portraitTablet));
+        assertFalse(SplitScreenUtils.isLeftRightSplit(false /* allowLeftRightSplitInPortrait */,
+                portraitPhone));
+
+        // Allow L/R split in portrait = true, only affects large screens
+        assertFalse(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                landscapeTablet));
+        assertTrue(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                landscapePhone));
+        assertTrue(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                portraitTablet));
+        assertFalse(SplitScreenUtils.isLeftRightSplit(true /* allowLeftRightSplitInPortrait */,
+                portraitPhone));
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index fff65f3..d819261 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -140,7 +140,7 @@
         when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
         when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
         when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
-        when(mSplitLayout.isLandscape()).thenReturn(false);
+        when(mSplitLayout.isLeftRightSplit()).thenReturn(false);
         when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true);
         when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash);