Initial commit of new split screen work.

TODO:
* Extract out common elements in TaskShortcutFactory.MultiWindowFactory
for commonalities between new and old way of
invoking split screen
* Integrate with WM APIs (b/182002789)
* Write tests for SplitSelectStateController

Bug: 181704764

Change-Id: Ice35adb4ea82897f5e2433dc9b93a549f3d511b5
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index cd64a94..55400a7 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -19,6 +19,14 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
+    <com.android.quickstep.views.SplitPlaceholderView
+        android:id="@+id/split_placeholder"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/split_placeholder_size"
+        android:background="@android:color/white"
+        android:alpha=".8"
+        android:visibility="gone" />
+
     <com.android.quickstep.fallback.RecentsDragLayer
         android:id="@+id/drag_layer"
         android:layout_width="match_parent"
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index fe57e9b..394e880 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -15,6 +15,14 @@
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <com.android.quickstep.views.SplitPlaceholderView
+        android:id="@+id/split_placeholder"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/split_placeholder_size"
+        android:background="@android:color/white"
+        android:alpha=".8"
+        android:visibility="gone" />
+
     <com.android.quickstep.views.LauncherRecentsView
         android:id="@+id/overview_panel"
         android:layout_width="match_parent"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 2a24624..755bce8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -39,6 +39,7 @@
     <dimen name="overview_grid_bottom_margin">90dp</dimen>
     <dimen name="overview_grid_side_margin">54dp</dimen>
     <dimen name="overview_grid_row_spacing">42dp</dimen>
+    <dimen name="split_placeholder_size">110dp</dimen>
 
     <dimen name="recents_page_spacing">16dp</dimen>
     <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 9a4487c..0764bb3 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -22,8 +22,6 @@
     <string name="derived_app_name" translatable="false">Quickstep</string>
 
     <!-- Options for recent tasks -->
-    <!-- Title for an option to enter split screen mode for a given app -->
-    <string name="recent_task_option_split_screen">Split screen</string>
     <!-- Title for an option to keep an app pinned to the screen until it is unpinned -->
     <string name="recent_task_option_pin">Pin</string>
     <!-- Title for an option to enter freeform mode for a given app -->
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index cdfd1a2..20a645e 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -58,8 +58,10 @@
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -215,7 +217,12 @@
 
         SysUINavigationMode.INSTANCE.get(this).updateMode();
         mActionsView = findViewById(R.id.overview_actions_view);
-        ((RecentsView) getOverviewPanel()).init(mActionsView);
+        SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
+        RecentsView overviewPanel = (RecentsView) getOverviewPanel();
+        splitPlaceholderView.init(
+                new SplitSelectStateController(SystemUiProxy.INSTANCE.get(this))
+        );
+        overviewPanel.init(mActionsView, splitPlaceholderView);
         mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
 
         addTaskbarIfNecessary();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 0f13ef9..bedaefa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -32,6 +32,7 @@
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.util.FloatProperty;
@@ -44,6 +45,7 @@
 import com.android.launcher3.graphics.OverviewScrim;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -105,7 +107,12 @@
                 config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
         setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
-        setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
+        PagedOrientationHandler orientationHandler =
+                ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
+        FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
+                TASK_PRIMARY_TRANSLATION, TASK_SECONDARY_TRANSLATION, mLauncher.getDeviceProfile());
+        setter.setFloat(mRecentsView, taskViewsFloat,
+                toState.getOverviewSecondaryTranslation(mLauncher),
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index c9de662..750f673 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -17,11 +17,14 @@
 
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
 import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.LauncherState.SPLIT_PLACHOLDER_VIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT;
 
 import android.annotation.TargetApi;
 import android.os.Build;
@@ -77,11 +80,26 @@
                     AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
         }
 
+        // Create or dismiss split screen select animations
+        LauncherState currentState = mLauncher.getStateManager().getState();
+        if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) {
+            builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
+        } else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) {
+            builder.add(mRecentsView.cancelSplitSelect(true).buildAnim());
+        }
+
         setAlphas(builder, config, toState);
         builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
                 toState.getOverviewFullscreenProgress(), LINEAR);
     }
 
+    /**
+     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     */
+    private boolean isSplitSelectionState(@NonNull LauncherState toState) {
+        return toState == OVERVIEW_SPLIT_SELECT;
+    }
+
     private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
             LauncherState state) {
         float clearAllButtonAlpha = (state.getVisibleElements(mLauncher) & CLEAR_ALL_BUTTON) != 0
@@ -93,6 +111,11 @@
         propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
                 MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
                         ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
+
+        float splitPlaceholderAlpha = state.areElementsVisible(mLauncher, SPLIT_PLACHOLDER_VIEW) ?
+                1 : 0;
+        propertySetter.setFloat(mRecentsView.getSplitPlaceholder(), ALPHA_FLOAT,
+                splitPlaceholderAlpha, LINEAR);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 1f68a04..372784a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -175,4 +175,12 @@
     public static OverviewState newModalTaskState(int id) {
         return new OverviewModalTaskState(id);
     }
+
+    /**
+     * New Overview substate representing state where 1 app for split screen has been selected and
+     * pinned and user is selecting the second one
+     */
+    public static OverviewState newSplitSelectState(int id) {
+        return new SplitScreenSelectState(id);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
new file mode 100644
index 0000000..722d74a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 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.launcher3.uioverrides.states;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * New Overview substate representing state where 1 app for split screen has been selected and
+ * pinned and user is selecting the second one
+ */
+public class SplitScreenSelectState extends OverviewState {
+    public SplitScreenSelectState(int id) {
+        super(id);
+    }
+
+    @Override
+    public void onBackPressed(Launcher launcher) {
+        launcher.getStateManager().goToState(OVERVIEW);
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return SPLIT_PLACHOLDER_VIEW;
+    }
+
+    @Override
+    public float getOverviewSecondaryTranslation(Launcher launcher) {
+        RecentsView recentsView = launcher.getOverviewPanel();
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+        int splitPosition = recentsView.getSplitPlaceholder().getSplitController()
+                .getActiveSplitPositionOption().mStagePosition;
+        int direction = orientationHandler.getSplitTranslationDirectionFactor(splitPosition);
+        return launcher.getResources().getDimension(R.dimen.split_placeholder_size) * direction;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3d68d64..d3ed791 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -63,7 +63,9 @@
 import com.android.quickstep.fallback.RecentsDragLayer;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
+import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.views.OverviewActionsView;
+import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -105,8 +107,14 @@
         mFallbackRecentsView = findViewById(R.id.overview_panel);
         mActionsView = findViewById(R.id.overview_actions_view);
 
+        SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
+        splitPlaceholderView.init(
+                new SplitSelectStateController(
+                        SystemUiProxy.INSTANCE.get(this))
+        );
+
         mDragLayer.recreateControllers();
-        mFallbackRecentsView.init(mActionsView);
+        mFallbackRecentsView.init(mActionsView, splitPlaceholderView);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 8636130..cd13200 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -36,11 +36,16 @@
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.quickstep.TaskShortcutFactory.SplitSelectSystemShortcut;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
@@ -57,11 +62,18 @@
  */
 public class TaskOverlayFactory implements ResourceBasedOverride {
 
-    public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
+    public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
+            DeviceProfile deviceProfile) {
         final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
         final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
         for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
             SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
+            if (menuOption == TaskShortcutFactory.SPLIT_SCREEN &&
+                    FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
+                addSplitOptions(shortcuts, activity, taskView, deviceProfile);
+                continue;
+            }
+
             if (shortcut != null) {
                 shortcuts.add(shortcut);
             }
@@ -91,6 +103,18 @@
         return shortcuts;
     }
 
+
+    public static void addSplitOptions(List<SystemShortcut> outShortcuts,
+            BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
+        PagedOrientationHandler orientationHandler =
+                taskView.getRecentsView().getPagedOrientationHandler();
+        List<SplitPositionOption> positions =
+                orientationHandler.getSplitPositionOptions(deviceProfile);
+        for (SplitPositionOption option : positions) {
+            outShortcuts.add(new SplitSelectSystemShortcut(activity, taskView, option));
+        }
+    }
+
     public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
         return new TaskOverlay(thumbnailView);
     }
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index d81f07f..c06e9a9 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -34,11 +34,13 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.popup.SystemShortcut.AppInfo;
 import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
@@ -58,7 +60,6 @@
  * Represents a system shortcut that can be shown for a recent task.
  */
 public interface TaskShortcutFactory {
-
     SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
 
     TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
@@ -93,6 +94,23 @@
         }
     }
 
+    class SplitSelectSystemShortcut extends SystemShortcut {
+        private final TaskView mTaskView;
+        private SplitPositionOption mSplitPositionOption;
+        public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView,
+                SplitPositionOption option) {
+            super(option.mIconResId, option.mTextResId, target, taskView.getItemInfo());
+            mTaskView = taskView;
+            mSplitPositionOption = option;
+            setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
+        }
+
+        @Override
+        public void onClick(View view) {
+            mTaskView.initiateSplitSelect(mSplitPositionOption);
+        }
+    }
+
     class MultiWindowSystemShortcut extends SystemShortcut {
 
         private Handler mHandler;
@@ -211,6 +229,16 @@
         }
 
         @Override
+        public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
+            SystemShortcut shortcut = super.getShortcut(activity, taskView);
+            if (FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
+                // Disable if there's only one recent app for split screen
+                shortcut.setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
+            }
+            return shortcut;
+        }
+
+        @Override
         protected ActivityOptions makeLaunchOptions(Activity activity) {
             final ActivityCompat act = new ActivityCompat(activity);
             final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 2feeffa..7a428ce 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -37,6 +38,7 @@
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
 import android.view.View;
@@ -295,6 +297,93 @@
         }
     }
 
+    /**
+     * TODO: This doesn't animate at present. Feel free to blow out everyhing in this method
+     * if needed
+     *
+     * We could manually try to animate the just the bounds for the leashes we get back, but we try
+     * to do it through TaskViewSimulator(TVS) since that handles a lot of the recents UI stuff for
+     * us.
+     *
+     * First you have to call TVS#setPreview() to indicate which leash it will operate one
+     * Then operations happen in TVS#apply() on each frame callback.
+     *
+     * TVS uses DeviceProfile to try to figure out things like task height and such based on if the
+     * device is in multiWindowMode or not. It's unclear given the two calls to startTask() when the
+     * device is considered in multiWindowMode and things like insets and stuff change
+     * and calculations have to be adjusted in the animations for that
+     */
+    public static void composeRecentsSplitLaunchAnimator(@NonNull AnimatorSet anim,
+            @NonNull TaskView v, @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
+            @NonNull StateManager stateManager, @NonNull DepthController depthController,
+            int targetStage) {
+        PendingAnimation out = new PendingAnimation(RECENTS_LAUNCH_DURATION);
+        boolean isRunningTask = v.isRunningTask();
+        TransformParams params = null;
+        TaskViewSimulator tvs = null;
+        RecentsView recentsView = v.getRecentsView();
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
+            params = recentsView.getLiveTileParams();
+            tvs = recentsView.getLiveTileTaskViewSimulator();
+        }
+
+        boolean inLiveTileMode =
+                ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1;
+        final RemoteAnimationTargets targets =
+                new RemoteAnimationTargets(appTargets, wallpaperTargets,
+                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+
+        if (params == null) {
+            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+            targets.addReleaseCheck(applier);
+
+            params = new TransformParams()
+                    .setSyncTransactionApplier(applier)
+                    .setTargetSet(targets);
+        }
+
+        Rect crop = new Rect();
+        Context context = v.getContext();
+        DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+        if (tvs == null && targets.apps.length > 0) {
+            tvs = new TaskViewSimulator(recentsView.getContext(), recentsView.getSizeStrategy());
+            tvs.setDp(dp);
+
+            // RecentsView never updates the display rotation until swipe-up so the value may
+            // be stale. Use the display value instead.
+            int displayRotation = DisplayController.getDefaultDisplay(recentsView.getContext())
+                    .getInfo().rotation;
+            tvs.getOrientationState().update(displayRotation, displayRotation);
+
+            tvs.setPreview(targets.apps[targets.apps.length - 1]);
+            tvs.fullScreenProgress.value = 0;
+            tvs.recentsViewScale.value = 1;
+//            tvs.setScroll(startScroll);
+
+            // Fade in the task during the initial 20% of the animation
+            out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
+                    clampToProgress(LINEAR, 0, 0.2f));
+        }
+
+        TaskViewSimulator topMostSimulator = null;
+
+        if (tvs != null) {
+            out.setFloat(tvs.fullScreenProgress,
+                    AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
+            out.setFloat(tvs.recentsViewScale,
+                    AnimatedFloat.VALUE, tvs.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
+            out.setInt(tvs, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
+
+            TaskViewSimulator finalTsv = tvs;
+            TransformParams finalParams = params;
+            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+            topMostSimulator = tvs;
+        }
+
+        anim.play(out.buildAnim());
+    }
+
     public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] appTargets,
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 13f6137..02fd5bb 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -34,6 +34,7 @@
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -56,8 +57,8 @@
     }
 
     @Override
-    public void init(OverviewActionsView actionsView) {
-        super.init(actionsView);
+    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+        super.init(actionsView, splitPlaceholderView);
         setOverviewStateEnabled(true);
         setOverlayEnabled(true);
     }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
new file mode 100644
index 0000000..d9154ed
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2021 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.quickstep.util;
+
+import android.animation.AnimatorSet;
+import android.app.ActivityOptions;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Pair;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.WrappedAnimationRunnerImpl;
+import com.android.launcher3.WrappedLauncherAnimationRunner;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Represent data needed for the transient state when user has selected one app for split screen
+ * and is in the process of either a) selecting a second app or b) exiting intention to invoke split
+ */
+public class SplitSelectStateController {
+
+    private final SystemUiProxy mSystemUiProxy;
+    private TaskView mInitialTaskView;
+    private SplitPositionOption mInitialPosition;
+
+    public SplitSelectStateController(SystemUiProxy systemUiProxy) {
+        mSystemUiProxy = systemUiProxy;
+    }
+
+    /**
+     * To be called after first task selected
+     */
+    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption) {
+        mInitialTaskView = taskView;
+        mInitialPosition = positionOption;
+    }
+
+    /**
+     * To be called after second task selected
+     */
+    public void setSecondTaskId(TaskView taskView) {
+        // Assume initial mInitialTaskId is for top/left part of screen
+        WrappedAnimationRunnerImpl initialSplitRunnerWrapped =  new SplitLaunchAnimationRunner(
+                mInitialTaskView, 0);
+        WrappedAnimationRunnerImpl secondarySplitRunnerWrapped =  new SplitLaunchAnimationRunner(
+                taskView, 1);
+        RemoteAnimationRunnerCompat initialSplitRunner = new WrappedLauncherAnimationRunner(
+                new Handler(Looper.getMainLooper()), initialSplitRunnerWrapped,
+                true /* startAtFrontOfQueue */);
+        RemoteAnimationRunnerCompat secondarySplitRunner = new WrappedLauncherAnimationRunner(
+                new Handler(Looper.getMainLooper()), secondarySplitRunnerWrapped,
+                true /* startAtFrontOfQueue */);
+        ActivityOptions initialOptions = ActivityOptionsCompat.makeRemoteAnimation(
+                new RemoteAnimationAdapterCompat(initialSplitRunner, 300, 150));
+        ActivityOptions secondaryOptions = ActivityOptionsCompat.makeRemoteAnimation(
+                new RemoteAnimationAdapterCompat(secondarySplitRunner, 300, 150));
+        mSystemUiProxy.startTask(mInitialTaskView.getTask().key.id, mInitialPosition.mStageType,
+                mInitialPosition.mStagePosition,
+                /*null*/ initialOptions.toBundle());
+        Pair<Integer, Integer> compliment = getComplimentaryStageAndPosition(mInitialPosition);
+        mSystemUiProxy.startTask(taskView.getTask().key.id, compliment.first,
+                compliment.second,
+                /*null*/ secondaryOptions.toBundle());
+        // After successful launch, call resetState
+        resetState();
+    }
+
+    @Nullable
+    public SplitPositionOption getActiveSplitPositionOption() {
+        return mInitialPosition;
+    }
+
+    /**
+     * @return the opposite stage and position from the {@param position} provided as first and
+     *         second object, respectively
+     * Ex. If position is has stage = Main and position = Top/Left, this will return
+     * Pair(stage=Side, position=Bottom/Left)
+     */
+    private Pair<Integer, Integer> getComplimentaryStageAndPosition(SplitPositionOption position) {
+        // Right now this is as simple as flipping between 0 and 1
+        int complimentStageType = position.mStageType ^ 1;
+        int complimentStagePosition = position.mStagePosition ^ 1;
+        return new Pair<>(complimentStageType, complimentStagePosition);
+    }
+
+    /**
+     * Remote animation runner for animation to launch an app.
+     */
+    private class SplitLaunchAnimationRunner implements WrappedAnimationRunnerImpl {
+
+        private final TaskView mV;
+        private final int mTargetState;
+
+        SplitLaunchAnimationRunner(TaskView v, int targetState) {
+            mV = v;
+            mTargetState = targetState;
+        }
+
+        @Override
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
+                RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
+                LauncherAnimationRunner.AnimationResult result) {
+            AnimatorSet anim = new AnimatorSet();
+            BaseQuickstepLauncher activity = BaseActivity.fromContext(mV.getContext());
+            TaskViewUtils.composeRecentsSplitLaunchAnimator(anim, mV,
+                    appTargets, wallpaperTargets, true, activity.getStateManager(),
+                    activity.getDepthController(), mTargetState);
+            result.setAnimation(anim, activity);
+        }
+    }
+
+
+    /**
+     * To be called if split select was cancelled
+     */
+    public void resetState() {
+        mInitialTaskView = null;
+        mInitialPosition = null;
+    }
+
+    public boolean isSplitSelectActive() {
+        return mInitialTaskView != null;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index df1229b..6e8a5f1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -104,6 +104,7 @@
     public final AnimatedFloat recentsViewScale = new AnimatedFloat();
     public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
     public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat();
+    public final AnimatedFloat recentsViewPrimaryTranslation = new AnimatedFloat();
     public final AnimatedFloat gridProgress = new AnimatedFloat();
     private final ScrollState mScrollState = new ScrollState();
 
@@ -347,6 +348,8 @@
         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewSecondaryTranslation.value);
+        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+                recentsViewPrimaryTranslation.value);
         applyWindowToHomeRotation(mMatrix);
 
         // Crop rect is the inverse of thumbnail matrix
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index ceb343d..9d31190 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -86,8 +86,8 @@
     }
 
     @Override
-    public void init(OverviewActionsView actionsView) {
-        super.init(actionsView);
+    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+        super.init(actionsView, splitPlaceholderView);
         setContentAlpha(0);
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b963025..3c5e403 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.Utilities.squaredHypot;
@@ -48,6 +49,7 @@
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
 import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
 import android.animation.LayoutTransition.TransitionListener;
@@ -114,6 +116,7 @@
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.OverScroller;
 import com.android.launcher3.util.ResourceBasedOverride.Overrides;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewPool;
 import com.android.quickstep.AnimatedFloat;
@@ -211,11 +214,17 @@
                 }
             };
 
+    /**
+     * Even though {@link TaskView} has distinct offsetTranslationX/Y and resistance property, they
+     * are currently both used to apply secondary translation. Should their use cases change to be
+     * more specific, we'd want to create a similar FloatProperty just for a TaskView's
+     * offsetX/Y property
+     */
     public static final FloatProperty<RecentsView> TASK_SECONDARY_TRANSLATION =
             new FloatProperty<RecentsView>("taskSecondaryTranslation") {
                 @Override
                 public void setValue(RecentsView recentsView, float v) {
-                    recentsView.setTaskViewsSecondaryTranslation(v);
+                    recentsView.setTaskViewsResistanceTranslation(v);
                 }
 
                 @Override
@@ -224,6 +233,25 @@
                 }
             };
 
+    /**
+     * Even though {@link TaskView} has distinct offsetTranslationX/Y and resistance property, they
+     * are currently both used to apply secondary translation. Should their use cases change to be
+     * more specific, we'd want to create a similar FloatProperty just for a TaskView's
+     * offsetX/Y property
+     */
+    public static final FloatProperty<RecentsView> TASK_PRIMARY_TRANSLATION =
+            new FloatProperty<RecentsView>("taskPrimaryTranslation") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskViewsPrimaryTranslation(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskViewsPrimaryTranslation;
+                }
+            };
+
     /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */
     public static final FloatProperty<RecentsView> RECENTS_SCALE_PROPERTY =
             new FloatProperty<RecentsView>("recentsScale") {
@@ -234,7 +262,8 @@
                     view.mLastComputedTaskPushOutDistance = null;
                     view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
                     view.updatePageOffsets();
-                    view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
+                    view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
+                    view.setTaskViewsPrimaryTranslation(view.mTaskViewsPrimaryTranslation);
                 }
 
                 @Override
@@ -306,6 +335,7 @@
 
     private float mAdjacentPageOffset = 0;
     private float mTaskViewsSecondaryTranslation = 0;
+    private float mTaskViewsPrimaryTranslation = 0;
     // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
     private float mGridProgress = 0;
     private boolean mShowAsGrid;
@@ -427,6 +457,28 @@
     private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
     private Layout mEmptyTextLayout;
 
+    /**
+     * Placeholder view indicating where the first split screen selected app will be placed
+     */
+    private SplitPlaceholderView mSplitPlaceholderView;
+    /**
+     * The first task that split screen selection was initiated with. When split select state is
+     * initialized, we create a
+     * {@link #createTaskDismissAnimation(TaskView, boolean, boolean, long)} for this TaskView but
+     * don't actually remove the task since the user might back out. As such, we also ensure this
+     * View doesn't go back into the {@link #mTaskViewPool}, see {@link #onViewRemoved(View)}
+     */
+    private TaskView mSplitHiddenTaskView;
+    /**
+     * Keeps track of the index of the TaskView that split screen was initialized with so we know
+     * where to insert it back into list of taskViews in case user backs out of entering split
+     * screen.
+     * NOTE: This index is the index while {@link #mSplitHiddenTaskView} was a child of recentsView,
+     * this doesn't get adjusted to reflect the new child count after the taskView is dismissed/
+     * removed from recentsView
+     */
+    private int mSplitHiddenTaskViewIndex;
+
     // Keeps track of the index where the first TaskView should be
     private int mTaskViewStartIndex = 0;
     private OverviewActionsView mActionsView;
@@ -582,9 +634,19 @@
         loadVisibleTaskData();
     }
 
-    public void init(OverviewActionsView actionsView) {
+    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
         mActionsView = actionsView;
         mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
+        mSplitPlaceholderView = splitPlaceholderView;
+
+    }
+
+    public SplitPlaceholderView getSplitPlaceholder() {
+        return mSplitPlaceholderView;
+    }
+
+    public boolean isSplitSelectionActive() {
+        return mSplitPlaceholderView.getSplitController().isSplitSelectActive();
     }
 
     @Override
@@ -628,8 +690,9 @@
     public void onViewRemoved(View child) {
         super.onViewRemoved(child);
 
-        // Clear the task data for the removed child if it was visible
-        if (child instanceof TaskView) {
+        // Clear the task data for the removed child if it was visible unless it's the initial
+        // taskview for entering split screen, we only pretend to dismiss the task
+        if (child instanceof TaskView && child != mSplitHiddenTaskView) {
             TaskView taskView = (TaskView) child;
             mHasVisibleTaskData.delete(taskView.getTask().key.id);
             mTaskViewPool.recycle(taskView);
@@ -707,6 +770,9 @@
             // Reset the running task when leaving overview since it can still have a reference to
             // its thumbnail
             mTmpRunningTask = null;
+            if (mSplitPlaceholderView.getSplitController().isSplitSelectActive()) {
+                cancelSplitSelect(false);
+            }
         }
     }
 
@@ -1764,7 +1830,7 @@
         // alpha is set to 0 so that it can be recycled in the view pool properly
         anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
         FloatProperty<TaskView> secondaryViewTranslate =
-                taskView.getDismissTaskTranslationProperty();
+                taskView.getSecondaryDissmissTranslationProperty();
         int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
         int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
 
@@ -1827,10 +1893,23 @@
                         offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
                     }
                 }
+
+                // Additional offset for fake landscape, if the pinning happens to the right or
+                // left, we need to scroll all the tasks away from the direction of the splaceholder
+                // view
+                if (isSplitSelectionActive()) {
+                    int splitPosition = getSplitPlaceholder().getSplitController()
+                            .getActiveSplitPositionOption().mStagePosition;
+                    int direction = mOrientationHandler
+                            .getSplitTranslationDirectionFactor(splitPosition);
+                    int splitOffset = mOrientationHandler.getSplitAnimationTranslation(
+                            mSplitPlaceholderView.getHeight(), mActivity.getDeviceProfile());
+                    offset += direction * splitOffset;
+                }
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
                 if (scrollDiff != 0) {
                     FloatProperty translationProperty = child instanceof TaskView
-                            ? ((TaskView) child).getFillDismissGapTranslationProperty()
+                            ? ((TaskView) child).getPrimaryDismissTranslationProperty()
                             : mOrientationHandler.getPrimaryViewTranslate();
 
                     ResourceProvider rp = DynamicResource.provider(mActivity);
@@ -1908,6 +1987,12 @@
                     onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
                 }
                 resetTaskVisuals();
+                if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
+                    // We want to keep the tasks translations in this temporary state
+                    // after resetting the rest above
+                    setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation);
+                    setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation);
+                }
                 mPendingAnimation = null;
             }
         });
@@ -2299,7 +2384,7 @@
         return distanceToOffscreen * offsetProgress;
     }
 
-    private void setTaskViewsSecondaryTranslation(float translation) {
+    private void setTaskViewsResistanceTranslation(float translation) {
         mTaskViewsSecondaryTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView task = getTaskViewAt(i);
@@ -2308,6 +2393,15 @@
         mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
     }
 
+    private void setTaskViewsPrimaryTranslation(float translation) {
+        mTaskViewsPrimaryTranslation = translation;
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView task = getTaskViewAt(i);
+            task.getPrimaryDismissTranslationProperty().set(task, translation / getScaleY());
+        }
+        mLiveTileTaskViewSimulator.recentsViewPrimaryTranslation.value = translation;
+    }
+
     /**
      * TODO: Do not assume motion across X axis for adjacent page
      */
@@ -2325,6 +2419,111 @@
         }
     }
 
+    public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
+        mSplitHiddenTaskView = taskView;
+        mSplitPlaceholderView.getSplitController().setInitialTaskSelect(taskView,
+                splitPositionOption);
+        mSplitHiddenTaskViewIndex = indexOfChild(taskView);
+        mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
+    }
+
+    public PendingAnimation createSplitSelectInitAnimation() {
+        int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
+        return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration);
+    }
+
+    public void confirmSplitSelect(TaskView taskView) {
+        mSplitPlaceholderView.getSplitController().setSecondTaskId(taskView);
+        resetTaskVisuals();
+        setTranslationY(0);
+    }
+
+    public PendingAnimation cancelSplitSelect(boolean animate) {
+        mSplitPlaceholderView.getSplitController().resetState();
+        int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
+        PendingAnimation pendingAnim = new PendingAnimation(duration);
+        if (!animate) {
+            resetFromSplitSelectionState();
+            return pendingAnim;
+        }
+
+        addViewInLayout(mSplitHiddenTaskView, mSplitHiddenTaskViewIndex,
+                mSplitHiddenTaskView.getLayoutParams());
+        mSplitHiddenTaskView.setAlpha(0);
+        int[] oldScroll = new int[getChildCount()];
+        getPageScrolls(oldScroll, false,
+                view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
+
+        // x is correct, y is before tasks move up
+        int[] locationOnScreen = mSplitHiddenTaskView.getLocationOnScreen();
+        int[] newScroll = new int[getChildCount()];
+        getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
+
+        boolean needsCurveUpdates = false;
+        for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
+            View child = getChildAt(i);
+            if (child == mSplitHiddenTaskView) {
+
+                int left = newScroll[i] + getPaddingStart();
+                int topMargin = mSplitHiddenTaskView.getThumbnailTopMargin();
+                int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1];
+                mSplitHiddenTaskView.layout(left, top,
+                        left + mSplitHiddenTaskView.getWidth(),
+                        top + mSplitHiddenTaskView.getHeight());
+                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, TRANSLATION_Y,
+                        -top + mSplitPlaceholderView.getHeight() - topMargin));
+                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
+            } else {
+                // If insertion is on last index (furthest from clear all), we directly add the view
+                // else we translate all views to the right of insertion index further right,
+                // ignore views to left
+                int scrollDiff = newScroll[i] - oldScroll[i];
+                if (scrollDiff != 0) {
+                    FloatProperty translationProperty = child instanceof TaskView
+                            ? ((TaskView) child).getPrimaryDismissTranslationProperty()
+                            : mOrientationHandler.getPrimaryViewTranslate();
+
+                    ResourceProvider rp = DynamicResource.provider(mActivity);
+                    SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
+                            .setDampingRatio(
+                                    rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio))
+                            .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness));
+                    pendingAnim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
+                            .setDuration(duration), ACCEL, sp);
+                    needsCurveUpdates = true;
+                }
+            }
+        }
+
+        if (needsCurveUpdates) {
+            pendingAnim.addOnFrameCallback(this::updateCurveProperties);
+        }
+
+        pendingAnim.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                resetFromSplitSelectionState();
+            }
+        });
+
+        return pendingAnim;
+    }
+
+    private void resetFromSplitSelectionState() {
+        mSplitHiddenTaskView.setTranslationY(0);
+        int pageToSnapTo = mCurrentPage;
+        if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
+            pageToSnapTo += 1;
+        } else {
+            pageToSnapTo = mSplitHiddenTaskViewIndex;
+        }
+        snapToPageImmediately(pageToSnapTo);
+        onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
+        resetTaskVisuals();
+        mSplitHiddenTaskView = null;
+        mSplitHiddenTaskViewIndex = -1;
+    }
+
     private void updateDeadZoneRects() {
         // Get the deadzone rect surrounding the clear all button to not dismiss overview to home
         mClearAllButtonDeadZoneRect.setEmpty();
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
new file mode 100644
index 0000000..fb9be81
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 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.quickstep.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.quickstep.util.SplitSelectStateController;
+
+public class SplitPlaceholderView extends View {
+
+    public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
+            new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
+                @Override
+                public void setValue(SplitPlaceholderView splitPlaceholderView, float v) {
+                    splitPlaceholderView.setVisibility(v != 0 ? VISIBLE : GONE);
+                    splitPlaceholderView.setAlpha(v);
+                }
+
+                @Override
+                public Float get(SplitPlaceholderView splitPlaceholderView) {
+                    return splitPlaceholderView.getAlpha();
+                }
+            };
+
+    private SplitSelectStateController mSplitController;
+
+    public SplitPlaceholderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void init(SplitSelectStateController controller) {
+        this.mSplitController = controller;
+    }
+
+    public SplitSelectStateController getSplitController() {
+        return mSplitController;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index fe7ece2..a5b7a5b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -186,7 +186,8 @@
         mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
         mTaskName.setOnClickListener(v -> close(true));
 
-        TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
+        TaskOverlayFactory.getEnabledShortcuts(taskView, mActivity.getDeviceProfile())
+                .forEach(this::addMenuOption);
     }
 
     private void addMenuOption(SystemShortcut menuOption) {
@@ -196,6 +197,8 @@
                 menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
         LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
         mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp);
+        menuOptionView.setEnabled(menuOption.isEnabled());
+        menuOptionView.setAlpha(menuOption.isEnabled() ? 1 : 0.5f);
         menuOptionView.setOnClickListener(view -> {
             if (LIVE_TILE.get()) {
                 RecentsView recentsView = mTaskView.getRecentsView();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index eace0f8..cd8ea76 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -28,6 +28,7 @@
 import static android.widget.Toast.LENGTH_SHORT;
 
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -84,6 +85,7 @@
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
@@ -345,7 +347,12 @@
                 });
                 anim.start();
             } else {
-                launchTaskAnimated();
+                if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
+                    // User tapped to select second split screen app
+                    getRecentsView().confirmSplitSelect(this);
+                } else {
+                    launchTaskAnimated();
+                }
             }
             mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
                     .log(LAUNCHER_TASK_LAUNCH_TAP);
@@ -591,6 +598,11 @@
     }
 
     private boolean showTaskMenu() {
+        if (getRecentsView().mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
+            // Don't show menu when selecting second split screen app
+            return true;
+        }
+
         if (!getRecentsView().isClearAllHidden()) {
             getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
         } else {
@@ -619,6 +631,10 @@
         }
     }
 
+    public int getThumbnailTopMargin() {
+        return (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
+    }
+
     public void setOrientationState(RecentsOrientedState orientationState) {
         PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
         boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
@@ -998,12 +1014,12 @@
         return Utilities.mapRange(progress, 0, endTranslation);
     }
 
-    public FloatProperty<TaskView> getFillDismissGapTranslationProperty() {
+    public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
         return getPagedOrientationHandler().getPrimaryValue(
                 DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
     }
 
-    public FloatProperty<TaskView> getDismissTaskTranslationProperty() {
+    public FloatProperty<TaskView> getSecondaryDissmissTranslationProperty() {
         return getPagedOrientationHandler().getSecondaryValue(
                 DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
     }
@@ -1081,7 +1097,8 @@
                         getContext().getText(R.string.accessibility_close)));
 
         final Context context = getContext();
-        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
+        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+                mActivity.getDeviceProfile())) {
             info.addAction(s.createAccessibilityAction(context));
         }
 
@@ -1113,7 +1130,8 @@
             return true;
         }
 
-        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
+        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+                mActivity.getDeviceProfile())) {
             if (s.hasHandlerForAction(action)) {
                 s.onClick(this);
                 return true;
@@ -1274,6 +1292,12 @@
         mSnapshotView.setOverlayEnabled(overlayEnabled);
     }
 
+    public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
+        RecentsView rv = getRecentsView();
+        getMenuView().close(false);
+        rv.initiateSplitSelect(this, splitPositionOption);
+    }
+
     /**
      * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
      */
diff --git a/quickstep/res/drawable/ic_split_screen.xml b/res/drawable/ic_split_screen.xml
similarity index 100%
rename from quickstep/res/drawable/ic_split_screen.xml
rename to res/drawable/ic_split_screen.xml
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 351182d..0600cae 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,6 +38,13 @@
     <!-- User visible name for the launcher/home screen. [CHAR_LIMIT=30] -->
     <string name="home_screen">Home</string>
 
+    <!-- Options for recent tasks -->
+    <!-- Title for an option to enter split screen mode for a given app -->
+    <string name="recent_task_option_split_screen">Split screen</string>
+    <string translatable="false" name="split_screen_position_top">Pin to top</string>
+    <string translatable="false" name="split_screen_position_left">Pin to left</string>
+    <string translatable="false" name="split_screen_position_right">Pin to right</string>
+
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
     <string name="long_press_widget_to_add">Touch &amp; hold to move a widget.</string>
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 21c40ef..aa97450 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
@@ -60,6 +61,7 @@
     public static final int TASKBAR = 1 << 7;
     public static final int CLEAR_ALL_BUTTON = 1 << 8;
     public static final int WORKSPACE_PAGE_INDICATOR = 1 << 9;
+    public static final int SPLIT_PLACHOLDER_VIEW = 1 << 10;
 
     /** Mask of all the items that are contained in the apps view. */
     public static final int APPS_VIEW_ITEM_MASK =
@@ -126,6 +128,8 @@
             OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL);
     public static final LauncherState BACKGROUND_APP =
             OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL);
+    public static final LauncherState OVERVIEW_SPLIT_SELECT =
+            OverviewState.newSplitSelectState(OVERVIEW_SPLIT_SELECT_ORDINAL);
 
     public final int ordinal;
 
@@ -241,6 +245,14 @@
     }
 
     /**
+     * For this state, how much additional vertical translation there should be for each of the
+     * child TaskViews.
+     */
+    public float getOverviewSecondaryTranslation(Launcher launcher) {
+        return 0;
+    }
+
+    /**
      * The amount of blur and wallpaper zoom to apply to the background of either the app
      * or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
      *
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 3657e89..48e41d5 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -205,6 +205,9 @@
             "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
             + "Only applicable on large screen devices.");
 
+    public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
+            "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 577fe4a..e5424cf 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -45,6 +45,11 @@
     protected final T mTarget;
     protected final ItemInfo mItemInfo;
 
+    /**
+     * Indicates if it's invokable or not through some disabled UI
+     */
+    private boolean isEnabled = true;
+
     public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
         mIconResId = iconResId;
         mLabelResId = labelResId;
@@ -83,6 +88,14 @@
                 mAccessibilityActionId, context.getText(mLabelResId));
     }
 
+    public void setEnabled(boolean enabled) {
+        isEnabled = enabled;
+    }
+
+    public boolean isEnabled() {
+        return isEnabled;
+    }
+
     public boolean hasHandlerForAction(int action) {
         return mAccessibilityActionId == action;
     }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 7cb6e34..f34bff6 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -32,6 +32,7 @@
     public static final int ALL_APPS_STATE_ORDINAL = 5;
     public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
     public static final int HINT_STATE_ORDINAL = 7;
+    public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 8;
     public static final String TAPL_EVENTS_TAG = "TaplEvents";
     public static final String SEQUENCE_MAIN = "Main";
     public static final String SEQUENCE_TIS = "TIS";
@@ -55,6 +56,8 @@
                 return "Background";
             case HINT_STATE_ORDINAL:
                 return "Hint";
+            case OVERVIEW_SPLIT_SELECT_ORDINAL:
+                return "OverviewSplitSelect";
             default:
                 return "Unknown";
         }
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 8a64f3d..c1cf0c8 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -21,6 +21,10 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_SIDE;
 
 import android.content.res.Resources;
 import android.graphics.PointF;
@@ -36,8 +40,13 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class LandscapePagedViewHandler implements PagedOrientationHandler {
 
@@ -212,6 +221,20 @@
     }
 
     @Override
+    public int getSplitTranslationDirectionFactor(int stagePosition) {
+        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    @Override
+    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
+        return translationOffset;
+    }
+
+    @Override
     public float getTaskMenuX(float x, View thumbnailView) {
         return thumbnailView.getMeasuredWidth() + x;
     }
@@ -282,4 +305,23 @@
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return rect.left;
     }
+
+    @Override
+    public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
+        List<SplitPositionOption> options = new ArrayList<>(2);
+        // Add left/right options where left => position top, right => position bottom
+        options.add(new SplitPositionOption(
+                R.drawable.ic_split_screen, R.string.split_screen_position_left,
+                STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+        options.add(new SplitPositionOption(
+                R.drawable.ic_split_screen, R.string.split_screen_position_right,
+                STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_SIDE));
+        return options;
+    }
+
+    @Override
+    public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
+            DeviceProfile deviceProfile) {
+        return primary;
+    }
 }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index e1cec87..fcfa205 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -32,6 +32,10 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+
+import java.util.List;
 
 /**
  * Abstraction layer to separate horizontal and vertical specific implementations
@@ -75,6 +79,8 @@
     int getScrollOffsetEnd(View view, Rect insets);
     int getPrimaryTranslationDirectionFactor();
     int getSecondaryTranslationDirectionFactor();
+    int getSplitTranslationDirectionFactor(@StagePosition int stagePosition);
+    int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp);
     ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
     void setMaxScroll(AccessibilityEvent event, int maxScroll);
     boolean getRecentsRtlSetting(Resources resources);
@@ -95,6 +101,9 @@
     int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate, LinearLayout taskMenuLayout);
     void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
     int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
+    List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
+    FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
+            DeviceProfile deviceProfile);
 
     // The following are only used by TaskViewTouchHandler.
     /** @return Either VERTICAL or HORIZONTAL. */
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index bcaf5f4..2bc2dc7 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -19,6 +19,10 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_SIDE;
 
 import android.content.res.Resources;
 import android.graphics.PointF;
@@ -34,8 +38,13 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class PortraitPagedViewHandler implements PagedOrientationHandler {
 
@@ -208,6 +217,23 @@
     }
 
     @Override
+    public int getSplitTranslationDirectionFactor(int stagePosition) {
+        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    @Override
+    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
+        if (dp.isLandscape) {
+            return translationOffset;
+        }
+        return 0;
+    }
+
+    @Override
     public float getTaskMenuX(float x, View thumbnailView) {
         return x;
     }
@@ -277,4 +303,35 @@
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return dp.heightPx - rect.bottom;
     }
+
+    @Override
+    public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
+        List<SplitPositionOption> options = new ArrayList<>(2);
+        // TODO: Add in correct icons
+        if (dp.isLandscape) { // or seascape
+            // Add left/right options
+            options.add(new SplitPositionOption(
+                    R.drawable.ic_split_screen, R.string.split_screen_position_left,
+                    STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+            options.add(new SplitPositionOption(
+                    R.drawable.ic_split_screen, R.string.split_screen_position_right,
+                    STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_SIDE));
+        } else {
+            // Only add top option
+            options.add(new SplitPositionOption(
+                    R.drawable.ic_split_screen, R.string.split_screen_position_top,
+                    STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+        }
+        return options;
+    }
+
+    @Override
+    public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
+            DeviceProfile dp) {
+        if (dp.isLandscape) { // or seascape
+            return primary;
+        } else {
+            return secondary;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 54af029..b5252f7 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -17,6 +17,10 @@
 package com.android.launcher3.touch;
 
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_SIDE;
 
 import android.content.res.Resources;
 import android.graphics.PointF;
@@ -25,7 +29,12 @@
 import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
 
@@ -35,6 +44,20 @@
     }
 
     @Override
+    public int getSplitTranslationDirectionFactor(int stagePosition) {
+        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    @Override
+    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
+        return translationOffset;
+    }
+
+    @Override
     public boolean getRecentsRtlSetting(Resources resources) {
         return Utilities.isRtl(resources);
     }
@@ -71,6 +94,19 @@
         return dp.widthPx - rect.right;
     }
 
+    @Override
+    public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
+        List<SplitPositionOption> options = new ArrayList<>(2);
+        // Add left/right options where left => position bottom, right => position top
+        options.add(new SplitPositionOption(
+                R.drawable.ic_split_screen, R.string.split_screen_position_left,
+                STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_SIDE));
+        options.add(new SplitPositionOption(
+                R.drawable.ic_split_screen, R.string.split_screen_position_right,
+                STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+        return options;
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
new file mode 100644
index 0000000..573c8bd
--- /dev/null
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021 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.launcher3.util;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+public final class SplitConfigurationOptions {
+
+    ///////////////////////////////////
+    // Taken from
+    // frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+    /**
+     * Stage position isn't specified normally meaning to use what ever it is currently set to.
+     */
+    public static final int STAGE_POSITION_UNDEFINED = -1;
+    /**
+     * Specifies that a stage is positioned at the top half of the screen if
+     * in portrait mode or at the left half of the screen if in landscape mode.
+     */
+    public static final int STAGE_POSITION_TOP_OR_LEFT = 0;
+
+    /**
+     * Specifies that a stage is positioned at the bottom half of the screen if
+     * in portrait mode or at the right half of the screen if in landscape mode.
+     */
+    public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+
+    @Retention(SOURCE)
+    @IntDef({STAGE_POSITION_UNDEFINED, STAGE_POSITION_TOP_OR_LEFT, STAGE_POSITION_BOTTOM_OR_RIGHT})
+    public @interface StagePosition {}
+
+    /**
+     * Stage type isn't specified normally meaning to use what ever the default is.
+     * E.g. exit split-screen and launch the app in fullscreen.
+     */
+    public static final int STAGE_TYPE_UNDEFINED = -1;
+    /**
+     * The main stage type.
+     */
+    public static final int STAGE_TYPE_MAIN = 0;
+
+    /**
+     * The side stage type.
+     */
+    public static final int STAGE_TYPE_SIDE = 1;
+
+    @IntDef({STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, STAGE_TYPE_SIDE})
+    public @interface StageType {}
+    ///////////////////////////////////
+
+    public static class SplitPositionOption {
+        public final int mIconResId;
+        public final int mTextResId;
+        @StagePosition
+        public final int mStagePosition;
+
+        @StageType
+        public final int mStageType;
+
+        public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) {
+            mIconResId = iconResId;
+            mTextResId = textResId;
+            mStagePosition = stagePosition;
+            mStageType = stageType;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
index 5b33f18..e413d7f 100644
--- a/src/com/android/launcher3/util/ViewPool.java
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -58,7 +58,7 @@
         Preconditions.assertUIThread();
         Handler handler = new Handler();
 
-        // LayoutInflater is not thread save as it maintains a global variable 'mConstructorArgs'.
+        // LayoutInflater is not thread safe as it maintains a global variable 'mConstructorArgs'.
         // Create a different copy to use on the background thread.
         LayoutInflater inflater = mInflater.cloneInContext(mInflater.getContext());
 
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index da5a94f..e85e505 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -49,4 +49,11 @@
     public static OverviewState newModalTaskState(int id) {
         return new OverviewState(id);
     }
+
+    /**
+     *  New Overview substate that represents the overview in modal mode (one task shown on its own)
+     */
+    public static OverviewState newSplitSelectState(int id) {
+        return new OverviewState(id);
+    }
 }