Merge "No code changes, this cl only reorganizes the functions" into tm-qpr-dev
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 02b83fe..951be4e 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -43,7 +43,8 @@
     <!-- for rotating surface by arbitrary degree -->
     <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
-    
+    <uses-permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA" />
+
     <!--
     Permissions required for read/write access to the workspace data. These permission name
     should not conflict with that defined in other apps, as such an app should embed its package
diff --git a/quickstep/res/drawable/ic_floating_task_button.xml b/quickstep/res/drawable/ic_floating_task_button.xml
deleted file mode 100644
index 63b2fd8..0000000
--- a/quickstep/res/drawable/ic_floating_task_button.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 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.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <group
-      android:pivotY="12"
-      android:pivotX="12"
-      android:scaleX=".75"
-      android:scaleY=".75">
-    <path
-        android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z"
-        android:fillColor="#636C6F"/>
-    <path
-        android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z"
-        android:fillColor="#636C6F"
-        android:fillType="evenOdd"/>
-  </group>
-</vector>
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index e493ac9..d5e3670 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -26,4 +26,5 @@
     android:importantForAccessibility="noHideDescendants"
     android:textColor="?priv-android:attr/textColorOnAccent"
     android:textSize="14sp"
-    android:autoSizeTextType="uniform"/>
\ No newline at end of file
+    android:autoSizeTextType="uniform"
+    android:autoSizeMaxTextSize="14sp"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_floating_task_button.xml b/quickstep/res/layout/taskbar_floating_task_button.xml
deleted file mode 100644
index b5beded..0000000
--- a/quickstep/res/layout/taskbar_floating_task_button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 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.launcher3.views.IconButtonView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/taskbar_icon_touch_size"
-    android:layout_height="@dimen/taskbar_icon_touch_size"
-    android:icon="@drawable/ic_floating_task_button"
-    />
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 45a2cf8..c0765ee 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -288,6 +288,10 @@
     <dimen name="transient_taskbar_margin">24dp</dimen>
     <dimen name="transient_taskbar_shadow_blur">40dp</dimen>
     <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
+    <!-- Taskbar swipe up thresholds -->
+    <dimen name="taskbar_app_window_threshold">150dp</dimen>
+    <dimen name="taskbar_home_overview_threshold">225dp</dimen>
+    <dimen name="taskbar_catch_up_threshold">300dp</dimen>
 
     <!--  Taskbar 3 button spacing  -->
     <dimen name="taskbar_button_space_inbetween">24dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java b/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java
deleted file mode 100644
index 5f4d239..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2022 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.taskbar;
-
-import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.R;
-
-// TODO: This would be replaced by the thing that has the role and provides the intent.
-/**
- * Helper to determine what intent should be used to display in a floating window, if one
- * exists.
- */
-public class FloatingTaskIntentResolver {
-    private static final String TAG = FloatingTaskIntentResolver.class.getSimpleName();
-
-    @Nullable
-    /** Gets an intent for a floating task, if one exists. */
-    public static Intent getIntent(Context context) {
-        PackageManager pm = context.getPackageManager();
-        String pkg = context.getString(R.string.floating_task_package);
-        String action = context.getString(R.string.floating_task_action);
-        if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(action)) {
-            Log.d(TAG, "intent could not be found, pkg= " + pkg + " action= " + action);
-            return null;
-        }
-        Intent intent = createIntent(pm, null, pkg, action);
-        if (intent != null) {
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            return intent;
-        }
-        Log.d(TAG, "No valid intent found!");
-        return null;
-    }
-
-    @Nullable
-    private static Intent createIntent(PackageManager pm, @Nullable String activityName,
-            String packageName, String action) {
-        if (TextUtils.isEmpty(activityName)) {
-            activityName = queryActivityForAction(pm, packageName, action);
-        }
-        if (TextUtils.isEmpty(activityName)) {
-            Log.d(TAG, "Activity name is empty even after action search: " + action);
-            return null;
-        }
-        ComponentName component = new ComponentName(packageName, activityName);
-        Intent intent = new Intent(action).setComponent(component).setPackage(packageName);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        Log.d(TAG, "createIntent returning: " + intent);
-        return intent;
-    }
-
-    @Nullable
-    private static String queryActivityForAction(PackageManager pm, String packageName,
-            String action) {
-        Intent intent = new Intent(action).setPackage(packageName);
-        ResolveInfo resolveInfo = pm.resolveActivity(intent, MATCH_DEFAULT_ONLY);
-        if (resolveInfo == null || resolveInfo.activityInfo == null) {
-            Log.d(TAG, "queryActivityForAction: + " + resolveInfo);
-            return null;
-        }
-        ActivityInfo info = resolveInfo.activityInfo;
-        if (!info.exported) {
-            Log.d(TAG, "queryActivityForAction: + " + info + " not exported");
-            return null;
-        }
-        if (!info.enabled) {
-            Log.d(TAG, "queryActivityForAction: + " + info + " not enabled");
-            return null;
-        }
-        return resolveInfo.activityInfo.name;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 16cc0ac..365ec75 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -219,7 +219,7 @@
         }
 
         int getIconLayoutBoundsWidth() {
-            return mControllers.taskbarViewController.getIconLayoutBounds().width();
+            return mControllers.taskbarViewController.getIconLayoutWidth();
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 49dba95..9ec8cfe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -97,6 +97,13 @@
         }
     }
 
+    /**
+     * Returns true iff taskbar is stashed.
+     */
+    public boolean isTaskbarStashed() {
+        return mControllers.taskbarStashController.isStashed();
+    }
+
     @CallSuper
     protected void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(String.format(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c754a59..d68d1db 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,14 +16,11 @@
 package com.android.launcher3.taskbar;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.os.SystemProperties;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -85,12 +82,6 @@
 
     private View mQsb;
 
-    // Only non-null when device supports having a floating task.
-    private @Nullable View mFloatingTaskButton;
-    private @Nullable Intent mFloatingTaskIntent;
-    private static final boolean FLOATING_TASKS_ENABLED =
-            SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
-
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -139,17 +130,6 @@
         // TODO: Disable touch events on QSB otherwise it can crash.
         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
 
-        if (FLOATING_TASKS_ENABLED) {
-            mFloatingTaskIntent = FloatingTaskIntentResolver.getIntent(context);
-            if (mFloatingTaskIntent != null) {
-                mFloatingTaskButton = LayoutInflater.from(context)
-                        .inflate(R.layout.taskbar_floating_task_button, this, false);
-                mFloatingTaskButton.setPadding(mItemPadding, mItemPadding, mItemPadding,
-                        mItemPadding);
-            } else {
-                Log.d(TAG, "Floating tasks is enabled but no intent was found!");
-            }
-        }
     }
 
     private int getColorWithGivenLuminance(int color, float luminance) {
@@ -177,10 +157,6 @@
         if (mAllAppsButton != null) {
             mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
         }
-        if (mFloatingTaskButton != null) {
-            mFloatingTaskButton.setOnClickListener(
-                    mControllerCallbacks.getFloatingTaskButtonListener(mFloatingTaskIntent));
-        }
     }
 
     private void removeAndRecycle(View view) {
@@ -205,9 +181,6 @@
         }
         removeView(mQsb);
 
-        if (mFloatingTaskButton != null) {
-            removeView(mFloatingTaskButton);
-        }
 
         for (int i = 0; i < hotseatItemInfos.length; i++) {
             ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -290,11 +263,6 @@
             mQsb.setVisibility(View.INVISIBLE);
         }
 
-        if (mFloatingTaskButton != null) {
-            int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
-            addView(mFloatingTaskButton, index);
-        }
-
         mThemeIconsBackground = calculateThemeIconsBackground();
         setThemedIconsBackgroundColor(mThemeIconsBackground);
     }
@@ -325,12 +293,8 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         int count = getChildCount();
-        int countExcludingQsb = count;
         DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
-        if (deviceProfile.isQsbInline) {
-            countExcludingQsb--;
-        }
-        int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+        int spaceNeeded = getIconLayoutWidth();
         int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
         boolean layoutRtl = isLayoutRtl();
         int iconEnd = right - (right - left - spaceNeeded) / 2;
@@ -421,6 +385,18 @@
     }
 
     /**
+     * Returns the space used by the icons
+     */
+    public int getIconLayoutWidth() {
+        int countExcludingQsb = getChildCount();
+        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+        if (deviceProfile.isQsbInline) {
+            countExcludingQsb--;
+        }
+        return countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+    }
+
+    /**
      * Returns the app icons currently shown in the taskbar.
      */
     public View[] getIconViews() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index f0277a4..c331857 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -25,7 +25,6 @@
 import static com.android.quickstep.AnimatedFloat.VALUE;
 
 import android.annotation.NonNull;
-import android.content.Intent;
 import android.graphics.Rect;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -55,7 +54,6 @@
 import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SystemUiProxy;
 
 import java.io.PrintWriter;
 import java.util.function.Predicate;
@@ -201,6 +199,10 @@
         return mTaskbarView.getIconLayoutBounds();
     }
 
+    public int getIconLayoutWidth() {
+        return mTaskbarView.getIconLayoutWidth();
+    }
+
     public View[] getIconViews() {
         return mTaskbarView.getIconViews();
     }
@@ -447,13 +449,6 @@
             };
         }
 
-        public View.OnClickListener getFloatingTaskButtonListener(@NonNull Intent intent) {
-            return v -> {
-                SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(v.getContext());
-                proxy.showFloatingTask(intent);
-            };
-        }
-
         public View.OnLongClickListener getIconOnLongClickListener() {
             return mControllers.taskbarDragController::startDragOnLongClick;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index eaf577b..b228fdb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -70,6 +70,7 @@
 import android.view.WindowManagerGlobal;
 import android.window.SplashScreen;
 
+import androidx.annotation.BinderThread;
 import androidx.annotation.Nullable;
 
 import com.android.app.viewcapture.ViewCapture;
@@ -135,6 +136,7 @@
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SplitWithKeyboardShortcutController;
 import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
@@ -179,6 +181,8 @@
     private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
     private @Nullable RotationChangeProvider mRotationChangeProvider;
     private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
+
+    private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
     /**
      * If Launcher restarted while in the middle of an Overview split select, it needs this data to
      * recover. In all other cases this will remain null.
@@ -194,11 +198,13 @@
         super.setupViews();
 
         mActionsView = findViewById(R.id.overview_actions_view);
-        RecentsView overviewPanel = (RecentsView) getOverviewPanel();
+        RecentsView overviewPanel = getOverviewPanel();
         SplitSelectStateController controller =
                 new SplitSelectStateController(this, mHandler, getStateManager(),
                         getDepthController(), getStatsLogManager());
         overviewPanel.init(mActionsView, controller);
+        mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
+                controller);
         mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
         mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
 
@@ -321,6 +327,17 @@
         super.showAllAppsFromIntent(alreadyOnHome);
     }
 
+    protected void onItemClicked(View view) {
+        if (!mSplitWithKeyboardShortcutController.handleSecondAppSelectionForSplit(view)) {
+            QuickstepLauncher.super.getItemOnClickListener().onClick(view);
+        }
+    }
+
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
+        return this::onItemClicked;
+    }
+
     @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
         Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
@@ -402,6 +419,7 @@
 
         super.onDestroy();
         mHotseatPredictionController.destroy();
+        mSplitWithKeyboardShortcutController.onDestroy();
         if (mViewCapture != null) mViewCapture.close();
     }
 
@@ -832,6 +850,12 @@
         return activityOptions;
     }
 
+    @Override
+    @BinderThread
+    public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+        mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
+    }
+
     /**
      * Adds a new launch cookie for the activity launch if supported.
      *
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index ff7c668..2e06825 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -64,6 +64,7 @@
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -101,6 +102,7 @@
 import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.launcher3.tracing.SwipeHandlerProto;
 import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory;
@@ -311,6 +313,10 @@
     // Interpolate RecentsView scale from start of quick switch scroll until this scroll threshold
     private final float mQuickSwitchScaleScrollThreshold;
 
+    private final int mTaskbarAppWindowThreshold;
+    private final int mTaskbarCatchUpThreshold;
+    private boolean mTaskbarAlreadyOpen;
+
     public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture,
@@ -331,11 +337,17 @@
         mTaskAnimationManager = taskAnimationManager;
         mTouchTimeMs = touchTimeMs;
         mContinuingLastGesture = continuingLastGesture;
-        mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
-                R.dimen.quick_switch_scaling_scroll_threshold);
 
-        mSplashMainWindowShiftLength = -context.getResources().getDimensionPixelSize(
-                R.dimen.starting_surface_exit_animation_window_shift_length);
+        Resources res = context.getResources();
+        mTaskbarAppWindowThreshold = res
+                .getDimensionPixelSize(R.dimen.taskbar_app_window_threshold);
+        mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
+
+        mQuickSwitchScaleScrollThreshold = res
+                .getDimension(R.dimen.quick_switch_scaling_scroll_threshold);
+
+        mSplashMainWindowShiftLength = -res
+                .getDimensionPixelSize(R.dimen.starting_surface_exit_animation_window_shift_length);
 
         initAfterSubclassConstructor();
         initStateCallbacks();
@@ -824,7 +836,7 @@
             return;
         }
         mLauncherTransitionController.setProgress(
-                Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
+                Math.max(getTaskbarProgress(), getScaleProgressDueToScroll()), mDragLengthFactor);
     }
 
     /**
@@ -1170,7 +1182,7 @@
     }
 
     private boolean hasReachedOverviewThreshold() {
-        return mCurrentShift.value > MIN_PROGRESS_FOR_OVERVIEW;
+        return getTaskbarProgress() > MIN_PROGRESS_FOR_OVERVIEW;
     }
 
     @UiThread
@@ -2198,7 +2210,7 @@
             AnimatorControllerWithResistance playbackController =
                     remoteHandle.getPlaybackController();
             if (playbackController != null) {
-                playbackController.setProgress(Math.max(mCurrentShift.value,
+                playbackController.setProgress(Math.max(getTaskbarProgress(),
                         getScaleProgressDueToScroll()), mDragLengthFactor);
             }
 
@@ -2242,6 +2254,41 @@
         return scaleProgress;
     }
 
+    /**
+     * Updates the current status of taskbar during this swipe.
+     */
+    public void setTaskbarAlreadyOpen(boolean taskbarAlreadyOpen) {
+        mTaskbarAlreadyOpen = taskbarAlreadyOpen;
+    }
+
+    /**
+     * Overrides the current shift progress to keep the app window at the bottom of the screen
+     * while the transient taskbar is being swiped in.
+     *
+     * There is also a catch up period so that the window can start moving 1:1 with the swipe.
+     */
+    private float getTaskbarProgress() {
+        if (!DisplayController.isTransientTaskbar(mContext)) {
+            return mCurrentShift.value;
+        }
+
+        if (mTaskbarAlreadyOpen) {
+            return mCurrentShift.value;
+        }
+
+        if (mCurrentDisplacement < mTaskbarAppWindowThreshold) {
+            return 0;
+        }
+
+        // "Catch up" with `mCurrentShift.value`.
+        if (mCurrentDisplacement < mTaskbarCatchUpThreshold) {
+            return Utilities.mapToRange(mCurrentDisplacement, mTaskbarAppWindowThreshold,
+                    mTaskbarCatchUpThreshold, 0, mCurrentShift.value, ACCEL_DEACCEL);
+        }
+
+        return mCurrentShift.value;
+    }
+
     private void setDividerShown(boolean shown, boolean immediate) {
         if (mDividerAnimator != null) {
             mDividerAnimator.cancel();
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index f591a1c..ddb06ce 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -65,6 +65,7 @@
     // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
     // visible.
     protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+    protected float mCurrentDisplacement;
 
     // The distance needed to drag to reach the task size in recents.
     protected int mTransitionDragLength;
@@ -116,6 +117,8 @@
     public void updateDisplacement(float displacement) {
         // We are moving in the negative x/y direction
         displacement = -displacement;
+        mCurrentDisplacement = displacement;
+
         float shift;
         if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
             shift = mDragLengthFactor;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 7705a25..8d9f11d 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -59,7 +59,6 @@
 import com.android.systemui.shared.system.smartspace.SmartspaceState;
 import com.android.wm.shell.back.IBackAnimation;
 import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.floating.IFloatingTasks;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.pip.IPipAnimationListener;
@@ -90,7 +89,6 @@
     private IPip mPip;
     private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
     private ISplitScreen mSplitScreen;
-    private IFloatingTasks mFloatingTasks;
     private IOneHanded mOneHanded;
     private IShellTransitions mShellTransitions;
     private IStartingWindow mStartingWindow;
@@ -168,7 +166,7 @@
     }
 
     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
-            IFloatingTasks floatingTasks, IOneHanded oneHanded, IShellTransitions shellTransitions,
+            IOneHanded oneHanded, IShellTransitions shellTransitions,
             IStartingWindow startingWindow, IRecentTasks recentTasks,
             ISysuiUnlockAnimationController sysuiUnlockAnimationController,
             IBackAnimation backAnimation, IDesktopMode desktopMode) {
@@ -176,7 +174,6 @@
         mSystemUiProxy = proxy;
         mPip = pip;
         mSplitScreen = splitScreen;
-        mFloatingTasks = floatingTasks;
         mOneHanded = oneHanded;
         mShellTransitions = shellTransitions;
         mStartingWindow = startingWindow;
@@ -210,7 +207,7 @@
     }
 
     public void clearProxy() {
-        setProxy(null, null, null, null, null, null, null, null, null, null, null);
+        setProxy(null, null, null, null, null, null, null, null, null, null);
     }
 
     // TODO(141886704): Find a way to remove this
@@ -696,20 +693,6 @@
     }
 
     //
-    // Floating tasks
-    //
-
-    public void showFloatingTask(Intent intent) {
-        if (mFloatingTasks != null) {
-            try {
-                mFloatingTasks.showTask(intent);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Launcher: Failed call showFloatingTask", e);
-            }
-        }
-    }
-
-    //
     // One handed
     //
 
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 9d5e7c3..e8722e2 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -43,7 +43,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
-import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Matrix;
@@ -399,9 +398,8 @@
      */
     public static void composeRecentsSplitLaunchAnimator(GroupedTaskView launchingTaskView,
             @NonNull StateManager stateManager, @Nullable DepthController depthController,
-            int initialTaskId, @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
-            @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
-            @NonNull Runnable finishCallback) {
+            int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo,
+            SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
         if (launchingTaskView != null) {
             AnimatorSet animatorSet = new AnimatorSet();
             animatorSet.addListener(new AnimatorListenerAdapter() {
@@ -491,8 +489,7 @@
      * If it is null, then it will simply fade in the starting apps and fade out launcher (for the
      * case where launcher handles animating starting split tasks from app icon) */
     public static void composeRecentsSplitLaunchAnimatorLegacy(
-            @Nullable GroupedTaskView launchingTaskView, int initialTaskId,
-            @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
+            @Nullable GroupedTaskView launchingTaskView, int initialTaskId, int secondTaskId,
             @NonNull RemoteAnimationTarget[] appTargets,
             @NonNull RemoteAnimationTarget[] wallpaperTargets,
             @NonNull RemoteAnimationTarget[] nonAppTargets,
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index e9f9d80..01dcd52 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -32,7 +32,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
@@ -114,7 +113,6 @@
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.wm.shell.back.IBackAnimation;
 import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.floating.IFloatingTasks;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.recents.IRecentTasks;
@@ -172,8 +170,6 @@
             IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
             ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
                     KEY_EXTRA_SHELL_SPLIT_SCREEN));
-            IFloatingTasks floatingTasks = IFloatingTasks.Stub.asInterface(bundle.getBinder(
-                    KEY_EXTRA_SHELL_FLOATING_TASKS));
             IOneHanded onehanded = IOneHanded.Stub.asInterface(
                     bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
             IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
@@ -191,7 +187,7 @@
                     bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
             MAIN_EXECUTOR.execute(() -> {
                 SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
-                        splitscreen, floatingTasks, onehanded, shellTransitions, startingWindow,
+                        splitscreen, onehanded, shellTransitions, startingWindow,
                         recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode);
                 TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
                 preloadOverview(true /* fromInit */);
@@ -298,6 +294,16 @@
             MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff);
         }
 
+        @BinderThread
+        @Override
+        public void enterStageSplitFromRunningApp(boolean leftOrTop) {
+            StatefulActivity activity =
+                    mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+            if (activity != null) {
+                activity.enterStageSplitFromRunningApp(leftOrTop);
+            }
+        }
+
         /**
          * Preloads the Overview activity.
          *
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 60d5ba4..9d269fb 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -37,6 +37,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.PointF;
 import android.os.Build;
 import android.util.Log;
@@ -48,13 +49,16 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AbsSwipeUpHandler;
 import com.android.quickstep.AbsSwipeUpHandler.Factory;
+import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationCallbacks;
@@ -97,6 +101,7 @@
     private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
     private final InputMonitorCompat mInputMonitorCompat;
     private final InputEventReceiver mInputEventReceiver;
+    private final BaseActivityInterface mActivityInterface;
 
     private final AbsSwipeUpHandler.Factory mHandlerFactory;
 
@@ -131,6 +136,10 @@
     // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
     private float mStartDisplacement;
 
+    private final boolean mIsTransientTaskbar;
+    private final boolean mTaskbarAlreadyOpen;
+    private final int mTaskbarHomeOverviewThreshold;
+
     public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
@@ -142,6 +151,11 @@
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
         mHandlerFactory = handlerFactory;
+        mActivityInterface = mGestureState.getActivityInterface();
+
+        Resources res = base.getResources();
+        mTaskbarHomeOverviewThreshold = res
+                .getDimensionPixelSize(R.dimen.taskbar_home_overview_threshold);
 
         mMotionPauseDetector = new MotionPauseDetector(base, false,
                 mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
@@ -153,6 +167,10 @@
         mInputMonitorCompat = inputMonitorCompat;
         mInputEventReceiver = inputEventReceiver;
 
+        TaskbarUIController controller = mActivityInterface.getTaskbarController();
+        mTaskbarAlreadyOpen = controller != null && !controller.isTaskbarStashed();
+        mIsTransientTaskbar = DisplayController.isTransientTaskbar(base);
+
         boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
         mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
 
@@ -279,6 +297,7 @@
                 float upDist = -displacement;
                 boolean passedSlop = squaredHypot(displacementX, displacementY)
                         >= mSquaredTouchSlop;
+
                 if (!mPassedSlopOnThisGesture && passedSlop) {
                     mPassedSlopOnThisGesture = true;
                 }
@@ -323,7 +342,13 @@
                     }
 
                     if (mDeviceState.isFullyGesturalNavMode()) {
-                        mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+                        float minDisplacement = mMotionPauseMinDisplacement;
+
+                        if (mIsTransientTaskbar && !mTaskbarAlreadyOpen) {
+                            minDisplacement += mTaskbarHomeOverviewThreshold;
+                        }
+
+                        mMotionPauseDetector.setDisallowPause(upDist < minDisplacement
                                 || isLikelyToStartNewTask);
                         mMotionPauseDetector.addPosition(ev);
                         mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
@@ -357,6 +382,8 @@
 
         // Notify the handler that the gesture has actually started
         mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
+
+        mInteractionHandler.setTaskbarAlreadyOpen(mTaskbarAlreadyOpen);
     }
 
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 3119a77..825c721 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -22,8 +22,10 @@
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
+import static com.android.launcher3.util.SplitConfigurationOptions.getOppositeStagePosition;
 
 import android.annotation.NonNull;
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
@@ -57,6 +59,7 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.FloatingTaskView;
 import com.android.quickstep.views.GroupedTaskView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
@@ -84,6 +87,8 @@
     private ItemInfo mItemInfo;
     private Intent mInitialTaskIntent;
     private int mInitialTaskId = INVALID_TASK_ID;
+    private String mInitialTaskPackageName;
+    private Intent mSecondTaskIntent;
     private int mSecondTaskId = INVALID_TASK_ID;
     private String mSecondTaskPackageName;
     private boolean mRecentsAnimationRunning;
@@ -95,6 +100,8 @@
     /** Represents where split is intended to be invoked from. */
     private StatsLogManager.EventEnum mSplitEvent;
 
+    private FloatingTaskView mFirstFloatingTaskView;
+
     public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
             DepthController depthController, StatsLogManager statsLogManager) {
         mContext = context;
@@ -106,19 +113,36 @@
     }
 
     /**
-     * To be called after first task selected
+     * To be called after first task selected in Overview.
      */
-    public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition,
+    public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
             StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
-        mInitialTaskId = taskId;
+        mInitialTaskId = task.key.id;
+        mInitialTaskPackageName = task.getTopComponent().getPackageName();
         setInitialData(stagePosition, splitEvent, itemInfo);
     }
 
+    /**
+     * To be called after first task selected from home or all apps.
+     */
     public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
             @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
         mInitialTaskIntent = intent;
         mUser = itemInfo.user;
         mItemInfo = itemInfo;
+        mInitialTaskPackageName = intent.getComponent().getPackageName();
+        setInitialData(stagePosition, splitEvent, itemInfo);
+    }
+
+    /**
+     * To be called after first task selected from using a split shortcut from the fullscreen
+     * running app.
+     */
+    public void setInitialTaskSelect(ActivityManager.RunningTaskInfo info,
+            @StagePosition int stagePosition, @NonNull ItemInfo itemInfo,
+            StatsLogManager.EventEnum splitEvent) {
+        mInitialTaskId = info.taskId;
+        mInitialTaskPackageName = info.topActivity.getPackageName();
         setInitialData(stagePosition, splitEvent, itemInfo);
     }
 
@@ -134,27 +158,11 @@
      * to be launched. Call after launcher side animations are complete.
      */
     public void launchSplitTasks(Consumer<Boolean> callback) {
-        final Intent fillInIntent;
-        if (mInitialTaskIntent != null) {
-            fillInIntent = new Intent();
-            if (TextUtils.equals(mInitialTaskIntent.getComponent().getPackageName(),
-                    mSecondTaskPackageName)) {
-                fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-            }
-        } else {
-            fillInIntent = null;
-        }
-
-        final PendingIntent pendingIntent = mInitialTaskIntent == null ? null : (mUser != null
-                ? PendingIntent.getActivityAsUser(mContext, 0, mInitialTaskIntent,
-                FLAG_MUTABLE, null /* options */, mUser)
-                : PendingIntent.getActivity(mContext, 0, mInitialTaskIntent, FLAG_MUTABLE));
-
         Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                 LogUtils.getShellShareableInstanceId();
-        launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
-                callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
-                instanceIds.first);
+        launchTasks(mInitialTaskId, mInitialTaskIntent, mInitialTaskPackageName, mSecondTaskId,
+                mSecondTaskIntent, mSecondTaskPackageName, mStagePosition, callback,
+                false /* freezeTaskList */, DEFAULT_SPLIT_RATIO, instanceIds.first);
 
         mStatsLogManager.logger()
                 .withItemInfo(mItemInfo)
@@ -162,23 +170,25 @@
                 .log(mSplitEvent);
     }
 
-
     /**
      * To be called as soon as user selects the second task (even if animations aren't complete)
      * @param task The second task that will be launched.
      */
     public void setSecondTask(Task task) {
         mSecondTaskId = task.key.id;
-        if (mInitialTaskIntent != null) {
-            mSecondTaskPackageName = task.getTopComponent().getPackageName();
-        }
+        mSecondTaskPackageName = task.getTopComponent().getPackageName();
+    }
+
+    public void setSecondTask(Intent intent) {
+        mSecondTaskIntent = intent;
+        mSecondTaskPackageName = intent.getComponent().getPackageName();
     }
 
     /**
      * To be called when we want to launch split pairs from an existing GroupedTaskView.
      */
-    public void launchTasks(GroupedTaskView groupedTaskView,
-            Consumer<Boolean> callback, boolean freezeTaskList) {
+    public void launchTasks(GroupedTaskView groupedTaskView, Consumer<Boolean> callback,
+            boolean freezeTaskList) {
         mLaunchingTaskView = groupedTaskView;
         TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
                 groupedTaskView.getTaskIdAttributeContainers();
@@ -194,22 +204,23 @@
      */
     public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
             Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
-        launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
-                stagePosition, callback, freezeTaskList, splitRatio, null);
+        launchTasks(taskId1, null /* intent1 */, null /* packageName1 */, taskId2,
+                null /* intent2 */, null /* packageName2 */, stagePosition, callback,
+                freezeTaskList, splitRatio, null);
     }
 
     /**
      * To be called when we want to launch split pairs from Overview. Split can be initiated from
      * either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
      * fill in intent with a taskId2 are set.
-     * @param taskPendingIntent is null when split is initiated from Overview
+     * @param intent1 is null when split is initiated from Overview
      * @param stagePosition representing location of task1
      * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that create
      *                   a split instance, null for cases that bring existing instaces to the
      *                   foreground (quickswitch, launching previous pairs from overview)
      */
-    public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
-            @Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
+    public void launchTasks(int taskId1, @Nullable Intent intent1, String packageName1, int taskId2,
+            @Nullable Intent intent2, String packageName2, @StagePosition int stagePosition,
             Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio,
             @Nullable InstanceId shellInstanceId) {
         TestLogging.recordEvent(
@@ -220,57 +231,107 @@
         }
         if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
             final RemoteSplitLaunchTransitionRunner animationRunner =
-                    new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2,
-                            callback);
+                    new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
             final RemoteTransitionCompat remoteTransition = new RemoteTransitionCompat(
                     animationRunner, MAIN_EXECUTOR,
                     ActivityThread.currentActivityThread().getApplicationThread());
-            if (taskPendingIntent == null) {
+            if (intent1 == null && intent2 == null) {
                 mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
                         null /* options2 */, stagePosition, splitRatio, remoteTransition,
                         shellInstanceId);
+            } else if (intent2 == null) {
+                launchIntentOrShortcut(intent1, packageName2, options1, taskId2, stagePosition,
+                        splitRatio, remoteTransition, shellInstanceId);
+            } else if (intent1 == null) {
+                launchIntentOrShortcut(intent2, packageName1, options1, taskId1,
+                        getOppositeStagePosition(stagePosition), splitRatio, remoteTransition,
+                        shellInstanceId);
             } else {
-                final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
-                        taskPendingIntent.getCreatorUserHandle());
-                if (shortcutInfo != null) {
-                    mSystemUiProxy.startShortcutAndTask(shortcutInfo,
-                            options1.toBundle(), taskId2, null /* options2 */, stagePosition,
-                            splitRatio, remoteTransition, shellInstanceId);
-                } else {
-                    mSystemUiProxy.startIntentAndTask(taskPendingIntent,
-                            fillInIntent, options1.toBundle(), taskId2, null /* options2 */,
-                            stagePosition, splitRatio, remoteTransition, shellInstanceId);
-                }
+                // TODO: the case when both split apps are started from an intent.
             }
         } else {
             final RemoteSplitLaunchAnimationRunner animationRunner =
-                    new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
-                            callback);
+                    new RemoteSplitLaunchAnimationRunner(taskId1, taskId2, callback);
             final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
                     RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
                     300, 150,
                     ActivityThread.currentActivityThread().getApplicationThread());
 
-            if (taskPendingIntent == null) {
+            if (intent1 == null && intent2 == null) {
                 mSystemUiProxy.startTasksWithLegacyTransition(taskId1, options1.toBundle(),
                         taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
                         shellInstanceId);
+            } else if (intent2 == null) {
+                launchIntentOrShortcutLegacy(intent1, packageName2, options1, taskId2,
+                        stagePosition, splitRatio, adapter, shellInstanceId);
+            } else if (intent1 == null) {
+                launchIntentOrShortcutLegacy(intent2, packageName1, options1, taskId1,
+                        getOppositeStagePosition(stagePosition), splitRatio, adapter,
+                        shellInstanceId);
             } else {
-                final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
-                        taskPendingIntent.getCreatorUserHandle());
-                if (shortcutInfo != null) {
-                    mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
-                            options1.toBundle(), taskId2, null /* options2 */, stagePosition,
-                            splitRatio, adapter, shellInstanceId);
-                } else {
-                    mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
-                            fillInIntent, options1.toBundle(), taskId2, null /* options2 */,
-                            stagePosition, splitRatio, adapter, shellInstanceId);
-                }
+                // TODO: the case when both split apps are started from an intent.
             }
         }
     }
 
+    private void launchIntentOrShortcut(Intent intent, String otherTaskPackageName,
+            ActivityOptions options1, int taskId, @StagePosition int stagePosition,
+            float splitRatio, RemoteTransitionCompat remoteTransition,
+            @Nullable InstanceId shellInstanceId) {
+        PendingIntent pendingIntent = getPendingIntent(intent);
+        final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+                pendingIntent.getCreatorUserHandle());
+        if (shortcutInfo != null) {
+            mSystemUiProxy.startShortcutAndTask(shortcutInfo,
+                    options1.toBundle(), taskId, null /* options2 */, stagePosition,
+                    splitRatio, remoteTransition, shellInstanceId);
+        } else {
+            mSystemUiProxy.startIntentAndTask(pendingIntent,
+                    getFillInIntent(intent, otherTaskPackageName), options1.toBundle(), taskId,
+                    null /* options2 */, stagePosition, splitRatio, remoteTransition,
+                    shellInstanceId);
+        }
+    }
+
+    private void launchIntentOrShortcutLegacy(Intent intent, String otherTaskPackageName,
+            ActivityOptions options1, int taskId, @StagePosition int stagePosition,
+            float splitRatio, RemoteAnimationAdapter adapter,
+            @Nullable InstanceId shellInstanceId) {
+        PendingIntent pendingIntent = getPendingIntent(intent);
+        final ShortcutInfo shortcutInfo = getShortcutInfo(intent,
+                pendingIntent.getCreatorUserHandle());
+        if (shortcutInfo != null) {
+            mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
+                    options1.toBundle(), taskId, null /* options2 */, stagePosition,
+                    splitRatio, adapter, shellInstanceId);
+        } else {
+            mSystemUiProxy.startIntentAndTaskWithLegacyTransition(pendingIntent,
+                    getFillInIntent(intent, otherTaskPackageName), options1.toBundle(), taskId,
+                    null /* options2 */, stagePosition, splitRatio, adapter,
+                    shellInstanceId);
+        }
+    }
+
+    private PendingIntent getPendingIntent(Intent intent) {
+        return intent == null ? null : (mUser != null
+                ? PendingIntent.getActivityAsUser(mContext, 0, intent,
+                FLAG_MUTABLE, null /* options */, mUser)
+                : PendingIntent.getActivity(mContext, 0, intent, FLAG_MUTABLE));
+    }
+
+    private Intent getFillInIntent(Intent intent, String otherTaskPackageName) {
+        if (intent == null) {
+            return null;
+        }
+
+        Intent fillInIntent = new Intent();
+        if (TextUtils.equals(intent.getComponent().getPackageName(), otherTaskPackageName)) {
+            fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        }
+        return fillInIntent;
+    }
+
+
     public @StagePosition int getActiveSplitStagePosition() {
         return mStagePosition;
     }
@@ -280,7 +341,7 @@
     }
 
     public void setRecentsAnimationRunning(boolean running) {
-        this.mRecentsAnimationRunning = running;
+        mRecentsAnimationRunning = running;
     }
 
     @Nullable
@@ -311,14 +372,12 @@
     private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
 
         private final int mInitialTaskId;
-        private final PendingIntent mInitialTaskPendingIntent;
         private final int mSecondTaskId;
         private final Consumer<Boolean> mSuccessCallback;
 
-        RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
-                int secondTaskId, Consumer<Boolean> callback) {
+        RemoteSplitLaunchTransitionRunner(int initialTaskId, int secondTaskId,
+                Consumer<Boolean> callback) {
             mInitialTaskId = initialTaskId;
-            mInitialTaskPendingIntent = initialTaskPendingIntent;
             mSecondTaskId = secondTaskId;
             mSuccessCallback = callback;
         }
@@ -327,12 +386,11 @@
         public void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
                 @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
             TaskViewUtils.composeRecentsSplitLaunchAnimator(mLaunchingTaskView, mStateManager,
-                    mDepthController, mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId,
-                    info, t, () -> {
-                    finishCallback.run();
-                    if (mSuccessCallback != null) {
-                        mSuccessCallback.accept(true);
-                    }
+                    mDepthController, mInitialTaskId, mSecondTaskId, info, t, () -> {
+                        finishCallback.run();
+                        if (mSuccessCallback != null) {
+                            mSuccessCallback.accept(true);
+                        }
                 });
             // After successful launch, call resetState
             resetState();
@@ -346,14 +404,12 @@
     private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
 
         private final int mInitialTaskId;
-        private final PendingIntent mInitialTaskPendingIntent;
         private final int mSecondTaskId;
         private final Consumer<Boolean> mSuccessCallback;
 
-        RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
-                int secondTaskId, Consumer<Boolean> successCallback) {
+        RemoteSplitLaunchAnimationRunner(int initialTaskId, int secondTaskId,
+                Consumer<Boolean> successCallback) {
             mInitialTaskId = initialTaskId;
-            mInitialTaskPendingIntent = initialTaskPendingIntent;
             mSecondTaskId = secondTaskId;
             mSuccessCallback = successCallback;
         }
@@ -364,9 +420,8 @@
                 Runnable finishedCallback) {
             postAsyncCallback(mHandler,
                     () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
-                            mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent,
-                            mSecondTaskId, apps, wallpapers, nonApps, mStateManager,
-                            mDepthController, () -> {
+                            mLaunchingTaskView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
+                            nonApps, mStateManager, mDepthController, () -> {
                                 finishedCallback.run();
                                 if (mSuccessCallback != null) {
                                     mSuccessCallback.accept(true);
@@ -394,7 +449,10 @@
     public void resetState() {
         mInitialTaskId = INVALID_TASK_ID;
         mInitialTaskIntent = null;
+        mInitialTaskPackageName = null;
         mSecondTaskId = INVALID_TASK_ID;
+        mSecondTaskIntent = null;
+        mSecondTaskPackageName = null;
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mRecentsAnimationRunning = false;
         mLaunchingTaskView = null;
@@ -407,7 +465,7 @@
      *         chosen
      */
     public boolean isSplitSelectActive() {
-        return isInitialTaskIntentSet() && mSecondTaskId == INVALID_TASK_ID;
+        return isInitialTaskIntentSet() && !isSecondTaskIntentSet();
     }
 
     /**
@@ -415,7 +473,7 @@
      *          be launched
      */
     public boolean isBothSplitAppsConfirmed() {
-        return isInitialTaskIntentSet() && mSecondTaskId != INVALID_TASK_ID;
+        return isInitialTaskIntentSet() && isSecondTaskIntentSet();
     }
 
     private boolean isInitialTaskIntentSet() {
@@ -425,4 +483,16 @@
     public int getInitialTaskId() {
         return mInitialTaskId;
     }
+
+    private boolean isSecondTaskIntentSet() {
+        return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null);
+    }
+
+    public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
+        mFirstFloatingTaskView = floatingTaskView;
+    }
+
+    public FloatingTaskView getFirstFloatingTaskView() {
+        return mFirstFloatingTaskView;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
new file mode 100644
index 0000000..3587bd1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 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 static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.view.View;
+
+import androidx.annotation.BinderThread;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.FloatingTaskView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
+public class SplitWithKeyboardShortcutController {
+
+    private final QuickstepLauncher mLauncher;
+    private final SplitSelectStateController mController;
+    private final OverviewComponentObserver mOverviewComponentObserver;
+
+    private final int mSplitPlaceholderSize;
+    private final int mSplitPlaceholderInset;
+
+    public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
+            SplitSelectStateController controller) {
+        mLauncher = launcher;
+        mController = controller;
+        RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
+                launcher.getApplicationContext());
+        mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
+                deviceState);
+
+        mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.split_placeholder_size);
+        mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.split_placeholder_inset);
+    }
+
+    @BinderThread
+    public void enterStageSplit(boolean leftOrTop) {
+        if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()) {
+            return;
+        }
+        RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
+                SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
+                false /* allowMinimizeSplitScreen */);
+        SplitWithKeyboardShortcutRecentsAnimationListener listener =
+                new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
+
+        MAIN_EXECUTOR.execute(() -> {
+            callbacks.addListener(listener);
+            UI_HELPER_EXECUTOR.execute(
+                    // Transition from fullscreen app to enter stage split in launcher with
+                    // recents animation.
+                    () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+                            mOverviewComponentObserver.getOverviewIntent(),
+                            SystemClock.uptimeMillis(), callbacks, null, null));
+        });
+    }
+
+    /**
+     * Handles second app selection from stage split. If the item can't be opened in split or
+     * it's not in stage split state, we pass it onto Launcher's default item click handler.
+     */
+    public boolean handleSecondAppSelectionForSplit(View view) {
+        if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
+                || !mController.isSplitSelectActive()) {
+            return false;
+        }
+        Object tag = view.getTag();
+        Intent intent;
+        if (tag instanceof WorkspaceItemInfo) {
+            final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
+            intent = workspaceItemInfo.intent;
+        } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
+            final com.android.launcher3.model.data.AppInfo appInfo =
+                    (com.android.launcher3.model.data.AppInfo) tag;
+            intent = appInfo.intent;
+        } else {
+            return false;
+        }
+        mController.setSecondTask(intent);
+        mController.launchSplitTasks(aBoolean -> mLauncher.getDragLayer().removeView(
+                mController.getFirstFloatingTaskView()));
+        return true;
+    }
+
+    public void onDestroy() {
+        mOverviewComponentObserver.onDestroy();
+    }
+
+    private class SplitWithKeyboardShortcutRecentsAnimationListener implements
+            RecentsAnimationCallbacks.RecentsAnimationListener {
+
+        private final boolean mLeftOrTop;
+        private final Rect mTempRect = new Rect();
+
+        private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
+            mLeftOrTop = leftOrTop;
+        }
+
+        @Override
+        public void onRecentsAnimationStart(RecentsAnimationController controller,
+                RecentsAnimationTargets targets) {
+            ActivityManager.RunningTaskInfo runningTaskInfo =
+                    ActivityManagerWrapper.getInstance().getRunningTask();
+            mController.setInitialTaskSelect(runningTaskInfo,
+                    mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
+                    null /* itemInfo */,
+                    mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
+                            : LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM);
+
+            RecentsView recentsView = mLauncher.getOverviewPanel();
+            recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
+                    mSplitPlaceholderSize, mSplitPlaceholderInset, mLauncher.getDeviceProfile(),
+                    mController.getActiveSplitStagePosition(), mTempRect);
+
+            PendingAnimation anim = new PendingAnimation(
+                    SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
+            RectF startingTaskRect = new RectF();
+            final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
+                    mLauncher, mLauncher.getDragLayer(),
+                    controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
+                    null /* icon */, startingTaskRect);
+            RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
+                    .getIconCache()
+                    .updateIconInBackground(
+                            Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
+                                    false /* isLocked */),
+                            (task) -> {
+                                if (task.thumbnail != null) {
+                                    floatingTaskView.setIcon(task.thumbnail.thumbnail);
+                                }
+                            });
+            floatingTaskView.setAlpha(1);
+            floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+                    false /* fadeWithThumbnail */, true /* isStagedTask */);
+            mController.setFirstFloatingTaskView(floatingTaskView);
+
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    controller.finish(true /* toRecents */, null /* onFinishComplete */,
+                            false /* sendUserLeaveHint */);
+                }
+            });
+            anim.buildAnim().start();
+        }
+    };
+}
diff --git a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
index 580cc99..3756b4a 100644
--- a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
+++ b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
@@ -26,7 +26,7 @@
     public int getPlaceholderIconFadeInStart() { return 167; }
     public int getPlaceholderIconFadeInEnd() { return 250; }
     public int getStagedRectSlideStart() { return 0; }
-    public int getStagedRectSlideEnd() { return 383; }
+    public int getStagedRectSlideEnd() { return 500; }
 
     public int getDuration() { return TABLET_CONFIRM_DURATION; }
 }
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index dc1ae52..1d421b2 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -11,6 +11,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -74,6 +75,7 @@
         }
     };
 
+    private int mSplitHolderSize;
     private FloatingTaskThumbnailView mThumbnailView;
     private SplitPlaceholderView mSplitPlaceholderView;
     private RectF mStartingPosition;
@@ -97,6 +99,9 @@
         mActivity = BaseActivity.fromContext(context);
         mIsRtl = Utilities.isRtl(getResources());
         mFullscreenParams = new FullscreenDrawParams(context);
+
+        mSplitHolderSize = context.getResources().getDimensionPixelSize(
+                R.dimen.split_placeholder_icon_size);
     }
 
     @Override
@@ -126,8 +131,7 @@
         RecentsView recentsView = launcher.getOverviewPanel();
         mOrientationHandler = recentsView.getPagedOrientationHandler();
         mStagePosition = recentsView.getSplitSelectController().getActiveSplitStagePosition();
-        mSplitPlaceholderView.setIcon(icon,
-                mContext.getResources().getDimensionPixelSize(R.dimen.split_placeholder_icon_size));
+        mSplitPlaceholderView.setIcon(icon, mSplitHolderSize);
         mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
     }
 
@@ -193,6 +197,10 @@
         mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
     }
 
+    public void setIcon(Bitmap icon) {
+        mSplitPlaceholderView.setIcon(new BitmapDrawable(icon), mSplitHolderSize);
+    }
+
     protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
         mStartingPosition.set(pos);
         lp.ignoreInsets = true;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ce96b71..e56b7b9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4184,7 +4184,7 @@
     public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
             StatsLogManager.EventEnum splitEvent) {
         mSplitHiddenTaskView = taskView;
-        mSplitSelectStateController.setInitialTaskSelect(taskView.getTask().key.id,
+        mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
                 stagePosition, splitEvent, taskView.getItemInfo());
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
         finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 9da3e87..b2a3a0d 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -30,6 +30,7 @@
         android:layout_height="wrap_content"
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="16dp"
+        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
         android:orientation="vertical">
 
         <TextView
@@ -40,7 +41,6 @@
             android:textSize="24sp"
             android:layout_marginTop="24dp"
             android:textColor="?android:attr/textColorSecondary"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:text="@string/widget_button_text"/>
 
         <FrameLayout
@@ -49,7 +49,6 @@
             android:layout_height="wrap_content"
             android:elevation="0.1dp"
             android:background="?android:attr/colorBackground"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:paddingBottom="8dp"
             android:clipToPadding="false"
             launcher:layout_sticky="true" >
@@ -63,7 +62,6 @@
             android:layout_marginTop="8dp"
             android:background="@drawable/widgets_recommendation_background"
             android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:visibility="gone" />
     </com.android.launcher3.views.StickyHeaderLayout>
 
diff --git a/res/values/config.xml b/res/values/config.xml
index 11b6e8c..d9b3da5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -205,6 +205,4 @@
     <!-- The max scale for the wallpaper when it's zoomed in -->
     <item name="config_wallpaperMaxScale" format="float" type="dimen">0</item>
 
-    <string name="floating_task_package" translatable="false"></string>
-    <string name="floating_task_action" translatable="false"></string>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0a28b6c..afdb071 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -380,6 +380,11 @@
     <dimen name="taskbar_button_margin_6_5">0dp</dimen>
     <dimen name="taskbar_button_margin_4_5">0dp</dimen>
     <dimen name="taskbar_button_margin_4_4">0dp</dimen>
+    <!-- Taskbar swipe up thresholds threshold -->
+    <dimen name="taskbar_nav_threshold">0dp</dimen>
+    <dimen name="taskbar_app_window_threshold">0dp</dimen>
+    <dimen name="taskbar_home_overview_threshold">0dp</dimen>
+    <dimen name="taskbar_catch_up_threshold">0dp</dimen>
 
     <!-- Size of the maximum radius for the enforced rounded rectangles. -->
     <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 98ecf3a..5225731 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
 import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
+import static com.android.launcher3.config.FeatureFlags.HOME_GARDENING_WORKSPACE_BUTTONS;
 
 import android.animation.TimeInterpolator;
 import android.content.Context;
@@ -118,7 +119,13 @@
             lp.rightMargin = (grid.widthPx - lp.width) / 2;
         }
         lp.height = grid.dropTargetBarSizePx;
-        lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+        // TODO: Add tablet support for DropTargetBar when HOME_GARDENING_WORKSPACE_BUTTONS flag
+        //  is on
+        if (HOME_GARDENING_WORKSPACE_BUTTONS.get()) {
+            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+        } else {
+            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+        }
 
         DeviceProfile dp = mLauncher.getDeviceProfile();
         int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7269d14..9426ae9 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -44,6 +44,7 @@
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
 import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
 import static com.android.launcher3.logging.StatsLogManager.EventEnum;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -142,6 +143,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.LauncherDragController;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.BitmapRenderer;
@@ -1191,7 +1193,6 @@
             mOverlayManager.onActivityResumed(this);
         }
 
-        AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
         DragView.removeAllViews(this);
         TraceHelper.INSTANCE.endSection(traceToken);
     }
@@ -1300,7 +1301,7 @@
 
     @Override
     public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        if (SHOW_DELIGHTFUL_PAGINATION.get()
+        if ((SHOW_DOT_PAGINATION.get() || SHOW_DELIGHTFUL_PAGINATION.get())
                 && WorkspacePageIndicator.class.getName().equals(name)) {
             return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
                     (ViewGroup) parent, false);
@@ -1331,7 +1332,7 @@
         BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.app_icon, parent, false);
         favorite.applyFromWorkspaceItem(info);
-        favorite.setOnClickListener(ItemClickHandler.INSTANCE);
+        favorite.setOnClickListener(getItemOnClickListener());
         favorite.setOnFocusChangeListener(mFocusHandler);
         return favorite;
     }
@@ -1704,6 +1705,10 @@
             outState.remove(RUNTIME_STATE_WIDGET_PANEL);
         }
 
+        // We close any open folders and shortcut containers that are not safe for rebind,
+        // and we need to make sure this state is reflected.
+        AbstractFloatingView.closeAllOpenViewsExcept(
+                this, isStarted() && !isForceInvisible(), TYPE_REBIND_SAFE);
         finishAutoCancelActionMode();
 
         if (mPendingRequestArgs != null) {
@@ -2814,16 +2819,30 @@
             }
 
             return v;
-        } else {
-            List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
-            containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
-            mWorkspace.forEachVisiblePage(page
-                    -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
-
-            // Order: Preferred item by itself or in folder, then by matching package/user
-            return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
-                    packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
         }
+
+        // Look for the item inside the folder at the current page
+        Folder folder = Folder.getOpen(this);
+        if (folder != null) {
+            View v = getFirstMatch(Collections.singletonList(
+                    folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
+                    preferredItem,
+                    packageAndUserAndApp);
+            if (v == null) {
+                folder.close(isStarted() && !isForceInvisible());
+            } else {
+                return v;
+            }
+        }
+
+        List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
+        containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
+        mWorkspace.forEachVisiblePage(page
+                -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+
+        // Order: Preferred item by itself or in folder, then by matching package/user
+        return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
+                packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
     }
 
     /**
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index b0ed2d2..6ba58b6 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -38,6 +38,7 @@
         public void add(Animator animatorSet) {
             animatorSet.setDuration(0);
             animatorSet.start();
+            animatorSet.end();
         }
     };
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 037a77e..694536c 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -253,6 +253,10 @@
             "ENABLE_SPLIT_FROM_WORKSPACE", true,
             "Enable initiating split screen from workspace.");
 
+    public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS =
+            getDebugFlag("ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", false,
+                    "Enable splitting from fullscreen app with keyboard shortcuts");
+
     public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(
             "ENABLE_NEW_MIGRATION_LOGIC", true,
             "Enable the new grid migration logic, keeping pages when src < dest");
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index bd01d51..fcc5d86 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -617,6 +617,11 @@
         @UiEvent(doc = "Number of apps in A-Z list (personal and work profile)")
         LAUNCHER_ALLAPPS_COUNT(1225),
 
+        @UiEvent(doc = "User has invoked split to right half with a keyboard shortcut.")
+        LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM(1232),
+
+        @UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.")
+        LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233)
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 3770de8..e9b6606 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.pageindicators;
 
 import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -182,7 +183,9 @@
 
     @Override
     public void setScroll(int currentScroll, int totalScroll) {
-        animatePaginationToAlpha(VISIBLE_ALPHA);
+        if (SHOW_DELIGHTFUL_PAGINATION.get() || SHOW_DOT_PAGINATION.get()) {
+            animatePaginationToAlpha(VISIBLE_ALPHA);
+        }
 
         if (mNumPages <= 1) {
             mCurrentScroll = 0;
@@ -193,9 +196,9 @@
             currentScroll = totalScroll - currentScroll;
         }
 
+        mTotalScroll = totalScroll;
         if (SHOW_DELIGHTFUL_PAGINATION.get()) {
             mCurrentScroll = currentScroll;
-            mTotalScroll = totalScroll;
             invalidate();
 
             if (mShouldAutoHide
@@ -214,9 +217,15 @@
         if (currentScroll < pageToLeftScroll + scrollThreshold) {
             // scroll is within the left page's threshold
             animateToPosition(pageToLeft);
+            if (SHOW_DOT_PAGINATION.get()) {
+                hideAfterDelay();
+            }
         } else if (currentScroll > pageToRightScroll - scrollThreshold) {
             // scroll is far enough from left page to go to the right page
             animateToPosition(pageToLeft + 1);
+            if (SHOW_DOT_PAGINATION.get()) {
+                hideAfterDelay();
+            }
         } else {
             // scroll is between left and right page
             animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 2a890c3..520f33c 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -231,4 +231,10 @@
      * etc.)
      */
     protected abstract void onHandleConfigurationChanged();
+
+    /**
+     * Enter staged split directly from the current running app.
+     * @param leftOrTop if the staged split will be positioned left or top.
+     */
+    public void enterStageSplitFromRunningApp(boolean leftOrTop) { }
 }
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 3eff783..19a3948 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -182,4 +182,12 @@
                 ? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
                 : LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
     }
+
+    public static @StagePosition int getOppositeStagePosition(@StagePosition int position) {
+        if (position == STAGE_POSITION_UNDEFINED) {
+            return position;
+        }
+        return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT
+                : STAGE_POSITION_TOP_OR_LEFT;
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index cf5f5fc..2695f67 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -489,6 +489,7 @@
 
     @Test
     @PortraitLandscape
+    @ScreenRecord // (b/256659409)
     public void testUninstallFromAllApps() throws Exception {
         TestUtil.installDummyApp();
         try {