Merge "Adds ENABLE_FLOATING_SEARCH_BOX flag for All Apps."
diff --git a/Android.bp b/Android.bp
index 0575602..7c78ba8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13,24 +13,12 @@
 // limitations under the License.
 
 package {
-    default_applicable_licenses: ["packages_apps_Launcher3_license"],
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 min_launcher3_sdk_version = "26"
 
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
-    name: "packages_apps_Launcher3_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-    ],
-    license_text: [
-        "NOTICE",
-    ],
-}
-
 android_library {
     name: "launcher-aosp-tapl",
     libs: [
diff --git a/Android.mk b/Android.mk
index ceaaf13..1bc8b28 100644
--- a/Android.mk
+++ b/Android.mk
@@ -49,7 +49,8 @@
 LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
+LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
 include $(BUILD_PACKAGE)
 
 #
@@ -85,7 +86,8 @@
 
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
+LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
 include $(BUILD_PACKAGE)
 
 
@@ -136,7 +138,8 @@
 LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
+LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
 include $(BUILD_PACKAGE)
 
 
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-2008, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 7b3e6c4..70b1438 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -14,11 +14,7 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_apps_Launcher3_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_apps_Launcher3_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 filegroup {
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 51c2294..af4f49d 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -21,7 +21,9 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.NO_OFFSET;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
@@ -63,6 +65,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.RecentsModel;
@@ -89,6 +92,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Stream;
 
@@ -494,8 +498,20 @@
 
     @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
-        return Stream.concat(Stream.of(WellbeingModel.SHORTCUT_FACTORY),
-                super.getSupportedShortcuts());
+        Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
+        if (ENABLE_SPLIT_FROM_WORKSPACE.get() && mDeviceProfile.isTablet) {
+            RecentsView recentsView = getOverviewPanel();
+            // TODO: Pull it out of PagedOrentationHandler for split from workspace.
+            List<SplitPositionOption> positions =
+                    recentsView.getPagedOrientationHandler().getSplitPositionOptions(
+                            mDeviceProfile);
+            List<SystemShortcut.Factory<BaseQuickstepLauncher>> splitShortcuts = new ArrayList<>();
+            for (SplitPositionOption position : positions) {
+                splitShortcuts.add(getSplitSelectShortcutByPosition(position));
+            }
+            base = Stream.concat(base, splitShortcuts.stream());
+        }
+        return Stream.concat(base, super.getSupportedShortcuts());
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
new file mode 100644
index 0000000..3bc6576
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -0,0 +1,95 @@
+/*
+ * 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.popup;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.quickstep.views.RecentsView;
+
+public interface QuickstepSystemShortcut {
+
+    String TAG = QuickstepSystemShortcut.class.getSimpleName();
+
+    static SystemShortcut.Factory<BaseQuickstepLauncher> getSplitSelectShortcutByPosition(
+            SplitPositionOption position) {
+        return (activity, itemInfo) -> new QuickstepSystemShortcut.SplitSelectSystemShortcut(
+                activity, itemInfo, position);
+    }
+
+    class SplitSelectSystemShortcut extends SystemShortcut<BaseQuickstepLauncher> {
+
+        private final BaseQuickstepLauncher mLauncher;
+        private final ItemInfo mItemInfo;
+        private final SplitPositionOption mPosition;
+
+        public SplitSelectSystemShortcut(BaseQuickstepLauncher launcher, ItemInfo itemInfo,
+                SplitPositionOption position) {
+            super(position.iconResId, position.textResId, launcher, itemInfo);
+
+            mLauncher = launcher;
+            mItemInfo = itemInfo;
+            mPosition = position;
+        }
+
+        @Override
+        public void onClick(View view) {
+            Bitmap bitmap;
+            Intent intent;
+            if (mItemInfo instanceof WorkspaceItemInfo) {
+                final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
+                bitmap = workspaceItemInfo.bitmap.icon;
+                intent = workspaceItemInfo.intent;
+            } else if (mItemInfo instanceof com.android.launcher3.model.data.AppInfo) {
+                final com.android.launcher3.model.data.AppInfo appInfo =
+                        (com.android.launcher3.model.data.AppInfo) mItemInfo;
+                bitmap = appInfo.bitmap.icon;
+                intent = appInfo.intent;
+            } else {
+                Log.e(TAG, "unknown item type");
+                return;
+            }
+
+            RecentsView recentsView = mLauncher.getOverviewPanel();
+            recentsView.initiateSplitSelect(
+                    new SplitSelectSource(view, new BitmapDrawable(bitmap), intent, mPosition));
+        }
+    }
+
+    class SplitSelectSource {
+
+        public final View view;
+        public final Drawable drawable;
+        public final Intent intent;
+        public final SplitPositionOption position;
+
+        public SplitSelectSource(View view, Drawable drawable, Intent intent,
+                SplitPositionOption position) {
+            this.view = view;
+            this.drawable = drawable;
+            this.intent = intent;
+            this.position = position;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 3242d42..d54b9e7 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -294,6 +294,11 @@
     }
 
     public void onOverlayScrollChanged(float progress) {
+        // Add some padding to the progress, such we don't change the depth on the last frames of
+        // the animation. It's possible that a user flinging the feed quickly would scroll
+        // horizontally by accident, causing the device to enter client composition unnecessarily.
+        progress = Math.min(progress * 1.1f, 1f);
+
         // Round out the progress to dedupe frequent, non-perceptable updates
         int progressI = (int) (progress * 256);
         float progressF = Utilities.boundToRange(progressI / 256f, 0f, 1f);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
index 0b53cc2..62125fe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.taskbar;
 
-import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
 
@@ -41,9 +40,6 @@
 
         mAppsView.setOnIconLongClickListener(
                 controllers.taskbarDragController::startDragOnLongClick);
-
-        // TODO(b/205803230): Remove once entry point button is implemented.
-        mContext.getDragLayer().findViewById(R.id.taskbar_view).setOnClickListener(v -> show());
     }
 
     /** Binds the current {@link AppInfo} instances to the {@link TaskbarAllAppsContainerView}. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 1761096..20762b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -27,10 +27,8 @@
 import android.animation.AnimatorSet;
 import android.annotation.Nullable;
 import android.content.SharedPreferences;
-import android.content.res.Resources;
 import android.view.ViewConfiguration;
 
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
@@ -146,10 +144,9 @@
     public TaskbarStashController(TaskbarActivityContext activity) {
         mActivity = activity;
         mPrefs = Utilities.getPrefs(mActivity);
-        final Resources resources = mActivity.getResources();
-        mStashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
         mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
         mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
+        mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize;
     }
 
     public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c8d9fca..fddd1ed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -22,6 +22,7 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.annotation.LayoutRes;
@@ -31,6 +32,7 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -38,6 +40,7 @@
 import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.AllAppsButton;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
@@ -64,6 +67,9 @@
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
+    // Only non-null when device supports having an All Apps button.
+    private @Nullable AllAppsButton mAllAppsButton;
+
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -94,6 +100,13 @@
 
         // Needed to draw folder leave-behind when opening one.
         setWillNotDraw(false);
+
+        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            mAllAppsButton = new AllAppsButton(context);
+            mAllAppsButton.setLayoutParams(
+                    new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+            mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+        }
     }
 
     protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
@@ -102,6 +115,10 @@
         mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
 
         setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener());
+
+        if (mAllAppsButton != null) {
+            mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
+        }
     }
 
     private void removeAndRecycle(View view) {
@@ -121,6 +138,10 @@
         int nextViewIndex = 0;
         int numViewsAnimated = 0;
 
+        if (mAllAppsButton != null) {
+            removeView(mAllAppsButton);
+        }
+
         for (int i = 0; i < hotseatItemInfos.length; i++) {
             ItemInfo hotseatItemInfo = hotseatItemInfos[i];
             if (hotseatItemInfo == null) {
@@ -191,6 +212,10 @@
         while (nextViewIndex < getChildCount()) {
             removeAndRecycle(getChildAt(nextViewIndex));
         }
+
+        if (mAllAppsButton != null) {
+            addView(mAllAppsButton);
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 0508994..778040d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -22,6 +22,7 @@
 
 import android.graphics.Rect;
 import android.util.FloatProperty;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -33,6 +34,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
@@ -45,6 +47,9 @@
  * Handles properties/data collection, then passes the results to TaskbarView to render.
  */
 public class TaskbarViewController implements TaskbarControllers.LoggableTaskbarController {
+
+    private static final String TAG = TaskbarViewController.class.getSimpleName();
+
     private static final Runnable NO_OP = () -> { };
 
     public static final int ALPHA_INDEX_HOME = 0;
@@ -225,14 +230,29 @@
         int count = mTaskbarView.getChildCount();
         for (int i = 0; i < count; i++) {
             View child = mTaskbarView.getChildAt(i);
-            ItemInfo info = (ItemInfo) child.getTag();
-            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+
+            int positionInHotseat = -1;
+            if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get() && i == count - 1) {
+                // Note that there is no All Apps button in the hotseat, this position is only used
+                // as its convenient for animation purposes.
+                positionInHotseat = mActivity.getDeviceProfile().inv.numShownHotseatIcons;
+
+                setter.setViewAlpha(child, 0, LINEAR);
+            } else if (child.getTag() instanceof ItemInfo) {
+                positionInHotseat = ((ItemInfo) child.getTag()).screenId;
+            } else {
+                Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
+                continue;
+            }
+
+            float hotseatIconCenter = hotseatPadding.left
+                    + (hotseatCellSize + borderSpacing) * positionInHotseat
+                    + hotseatCellSize / 2;
 
             float childCenter = (child.getLeft() + child.getRight()) / 2;
-            float hotseatIconCenter = hotseatPadding.left
-                    + (hotseatCellSize + borderSpacing) * info.screenId
-                    + hotseatCellSize / 2;
             setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+
+            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
         }
 
         AnimatorPlaybackController controller = setter.createPlaybackController();
@@ -279,6 +299,10 @@
             return mActivity.getItemOnClickListener();
         }
 
+        public View.OnClickListener getAllAppsButtonClickListener() {
+            return v -> mControllers.taskbarAllAppsViewController.show();
+        }
+
         public View.OnLongClickListener getIconOnLongClickListener() {
             return mControllers.taskbarDragController::startDragOnLongClick;
         }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 22a30e9..4aa69d1 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -608,6 +608,21 @@
         }
     }
 
+    public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+            Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions,
+            Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
+            float splitRatio, RemoteAnimationAdapter adapter) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
+                        taskId, intentFirst, mainOptions, sideOptions, sidePosition, splitRatio,
+                        adapter);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTasksWithLegacyTransition");
+            }
+        }
+    }
+
     public void startShortcut(String packageName, String shortcutId, int position,
             Bundle options, UserHandle user) {
         if (mSplitScreen != null) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 5afcdcb..5b69aeb 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -46,6 +47,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Matrix;
@@ -389,18 +391,20 @@
      * 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 Task initalTask,
-            @NonNull Task secondTask, @NonNull TransitionInfo transitionInfo,
-            SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
-
-        final TransitionInfo.Change[] splitRoots = new TransitionInfo.Change[2];
+    public static void composeRecentsSplitLaunchAnimator(int initialTaskId,
+            @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
+            @NonNull TransitionInfo transitionInfo, SurfaceControl.Transaction t,
+            @NonNull Runnable finishCallback) {
+        // TODO: consider initialTaskPendingIntent
+        TransitionInfo.Change splitRoot1 = null;
+        TransitionInfo.Change splitRoot2 = null;
         for (int i = 0; i < transitionInfo.getChanges().size(); ++i) {
             final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
             final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
             final int mode = change.getMode();
             // Find the target tasks' root tasks since those are the split stages that need to
             // be animated (the tasks themselves are children and thus inherit animation).
-            if (taskId == initalTask.key.id || taskId == secondTask.key.id) {
+            if (taskId == initialTaskId || taskId == secondTaskId) {
                 if (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
                     throw new IllegalStateException(
                             "Expected task to be showing, but it is " + mode);
@@ -409,16 +413,18 @@
                     throw new IllegalStateException("Initiating multi-split launch but the split"
                             + "root of " + taskId + " is already visible or has broken hierarchy.");
                 }
-                splitRoots[taskId == initalTask.key.id ? 0 : 1] =
-                        transitionInfo.getChange(change.getParent());
+            }
+            if (taskId == initialTaskId && initialTaskId != INVALID_TASK_ID) {
+                splitRoot1 = transitionInfo.getChange(change.getParent());
+            }
+            if (taskId == secondTaskId) {
+                splitRoot2 = transitionInfo.getChange(change.getParent());
             }
         }
 
         // This is where we should animate the split roots. For now, though, just make them visible.
-        for (int i = 0; i < 2; ++i) {
-            t.show(splitRoots[i].getLeash());
-            t.setAlpha(splitRoots[i].getLeash(), 1.f);
-        }
+        animateSplitRoot(t, splitRoot1);
+        animateSplitRoot(t, splitRoot2);
 
         // This contains the initial state (before animation), so apply this at the beginning of
         // the animation.
@@ -428,6 +434,14 @@
         finishCallback.run();
     }
 
+    private static void animateSplitRoot(SurfaceControl.Transaction t,
+            TransitionInfo.Change splitRoot) {
+        if (splitRoot != null) {
+            t.show(splitRoot.getLeash());
+            t.setAlpha(splitRoot.getLeash(), 1.f);
+        }
+    }
+
     /**
      * Legacy version (until shell transitions are enabled)
      *
@@ -440,9 +454,9 @@
      * 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,
-            @NonNull Task initialTask,
-            @NonNull Task secondTask, @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @Nullable GroupedTaskView launchingTaskView, int initialTaskId,
+            @Nullable PendingIntent initialTaskPendingIntent, int secondTaskId,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
             @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
             @NonNull StateManager stateManager,
@@ -478,7 +492,7 @@
 
             if (mode == MODE_OPENING) {
                 openingTargets.add(leash);
-            } else if (taskId == initialTask.key.id || taskId == secondTask.key.id) {
+            } else if (taskId == initialTaskId || taskId == secondTaskId) {
                 throw new IllegalStateException("Expected task to be opening, but it is " + mode);
             } else if (mode == MODE_CLOSING) {
                 closingTargets.add(leash);
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 69da977..c7a8382 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.launcher3.AbstractFloatingView;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.FallbackActivityInterface;
@@ -254,4 +255,10 @@
         // Do not let touch escape to siblings below this view.
         return result || mActivity.getStateManager().getState().overviewUi();
     }
+
+    @Override
+    public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
+        super.initiateSplitSelect(splitSelectSource);
+        mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 5253e8c..e856d8a 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep.util;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
 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;
@@ -24,7 +26,8 @@
 
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
-import android.graphics.Rect;
+import android.app.PendingIntent;
+import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
 import android.view.RemoteAnimationAdapter;
@@ -42,7 +45,6 @@
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.views.GroupedTaskView;
 import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -62,16 +64,16 @@
     private final StateManager mStateManager;
     private final DepthController mDepthController;
     private @StagePosition int mStagePosition;
-    private Task mInitialTask;
-    private Task mSecondTask;
+    private PendingIntent mInitialTaskPendingIntent;
+    private int mInitialTaskId = INVALID_TASK_ID;
+    private int mSecondTaskId = INVALID_TASK_ID;
     private boolean mRecentsAnimationRunning;
     /** If not null, this is the TaskView we want to launch from */
     @Nullable
     private GroupedTaskView mLaunchingTaskView;
 
     public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy,
-            StateManager stateManager,
-            DepthController depthController) {
+            StateManager stateManager, DepthController depthController) {
         mHandler = handler;
         mSystemUiProxy = systemUiProxy;
         mStateManager = stateManager;
@@ -81,19 +83,26 @@
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
-            Rect initialBounds) {
-        mInitialTask = task;
+    public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) {
+        mInitialTaskId = taskId;
         mStagePosition = stagePosition;
+        mInitialTaskPendingIntent = null;
+    }
+
+    public void setInitialTaskSelect(PendingIntent pendingIntent,
+            @StagePosition int stagePosition) {
+        mInitialTaskPendingIntent = pendingIntent;
+        mStagePosition = stagePosition;
+        mInitialTaskId = INVALID_TASK_ID;
     }
 
     /**
      * To be called after second task selected
      */
-    public void setSecondTaskId(Task task, Consumer<Boolean> callback) {
-        mSecondTask = task;
-        launchTasks(mInitialTask, mSecondTask, mStagePosition, callback,
-                false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
+    public void setSecondTaskId(int taskId, Consumer<Boolean> callback) {
+        mSecondTaskId = taskId;
+        launchTasks(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, mStagePosition,
+                callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
     }
 
     /**
@@ -104,7 +113,8 @@
         mLaunchingTaskView = groupedTaskView;
         TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
                 groupedTaskView.getTaskIdAttributeContainers();
-        launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(),
+        launchTasks(taskIdAttributeContainers[0].getTask().key.id, null,
+                taskIdAttributeContainers[1].getTask().key.id,
                 taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList,
                 groupedTaskView.getSplitRatio());
     }
@@ -112,22 +122,25 @@
     /**
      * @param stagePosition representing location of task1
      */
-    public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
-            Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+    public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
+            int taskId2, @StagePosition int stagePosition, Consumer<Boolean> callback,
+            boolean freezeTaskList, float splitRatio) {
         // Assume initial task is for top/left part of screen
         final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
-                ? new int[]{task1.key.id, task2.key.id}
-                : new int[]{task2.key.id, task1.key.id};
+                ? new int[]{taskId1, taskId2}
+                : new int[]{taskId2, taskId1};
         if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
             RemoteSplitLaunchTransitionRunner animationRunner =
-                    new RemoteSplitLaunchTransitionRunner(task1, task2);
+                    new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2);
             mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
                     null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
                     new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
                             ActivityThread.currentActivityThread().getApplicationThread()));
+            // TODO: handle intent + task with shell transition
         } else {
             RemoteSplitLaunchAnimationRunner animationRunner =
-                    new RemoteSplitLaunchAnimationRunner(task1, task2, callback);
+                    new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2,
+                            callback);
             final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
                     RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
                     300, 150,
@@ -137,9 +150,16 @@
             if (freezeTaskList) {
                 mainOpts.setFreezeRecentTasksReordering();
             }
-            mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
-                    taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
-                    splitRatio, adapter);
+            if (taskPendingIntent == null) {
+                mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
+                        taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+                        splitRatio, adapter);
+            } else {
+                mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
+                        new Intent(), taskId2, stagePosition == STAGE_POSITION_TOP_OR_LEFT,
+                        mainOpts.toBundle(), null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+                        splitRatio, adapter);
+            }
         }
     }
 
@@ -156,19 +176,22 @@
      */
     private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner {
 
-        private final Task mInitialTask;
-        private final Task mSecondTask;
+        private final int mInitialTaskId;
+        private final PendingIntent mInitialTaskPendingIntent;
+        private final int mSecondTaskId;
 
-        RemoteSplitLaunchTransitionRunner(Task initialTask, Task secondTask) {
-            mInitialTask = initialTask;
-            mSecondTask = secondTask;
+        RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
+                int secondTaskId) {
+            mInitialTaskId = initialTaskId;
+            mInitialTaskPendingIntent = initialTaskPendingIntent;
+            mSecondTaskId = secondTaskId;
         }
 
         @Override
         public void startAnimation(IBinder transition, TransitionInfo info,
                 SurfaceControl.Transaction t, Runnable finishCallback) {
-            TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTask,
-                    mSecondTask, info, t, finishCallback);
+            TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskId,
+                    mInitialTaskPendingIntent, mSecondTaskId, info, t, finishCallback);
             // After successful launch, call resetState
             resetState();
         }
@@ -180,14 +203,16 @@
      */
     private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat {
 
-        private final Task mInitialTask;
-        private final Task mSecondTask;
+        private final int mInitialTaskId;
+        private final PendingIntent mInitialTaskPendingIntent;
+        private final int mSecondTaskId;
         private final Consumer<Boolean> mSuccessCallback;
 
-        RemoteSplitLaunchAnimationRunner(Task initialTask, Task secondTask,
-                Consumer<Boolean> successCallback) {
-            mInitialTask = initialTask;
-            mSecondTask = secondTask;
+        RemoteSplitLaunchAnimationRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
+                int secondTaskId, Consumer<Boolean> successCallback) {
+            mInitialTaskId = initialTaskId;
+            mInitialTaskPendingIntent = initialTaskPendingIntent;
+            mSecondTaskId = secondTaskId;
             mSuccessCallback = successCallback;
         }
 
@@ -197,8 +222,9 @@
                 Runnable finishedCallback) {
             postAsyncCallback(mHandler,
                     () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
-                            mLaunchingTaskView, mInitialTask, mSecondTask, apps, wallpapers,
-                            nonApps, mStateManager, mDepthController, () -> {
+                            mLaunchingTaskView, mInitialTaskId, mInitialTaskPendingIntent,
+                            mSecondTaskId, apps, wallpapers, nonApps, mStateManager,
+                            mDepthController, () -> {
                                 finishedCallback.run();
                                 if (mSuccessCallback != null) {
                                     mSuccessCallback.accept(true);
@@ -224,8 +250,9 @@
      * To be called if split select was cancelled
      */
     public void resetState() {
-        mInitialTask = null;
-        mSecondTask = null;
+        mInitialTaskId = INVALID_TASK_ID;
+        mInitialTaskPendingIntent = null;
+        mSecondTaskId = INVALID_TASK_ID;
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mRecentsAnimationRunning = false;
         mLaunchingTaskView = null;
@@ -236,6 +263,7 @@
      *         chosen
      */
     public boolean isSplitSelectActive() {
-        return mInitialTask != null && mSecondTask == null;
+        return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskPendingIntent != null)
+                && mSecondTaskId == INVALID_TASK_ID;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 18ab3bb..9ae5d25 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -7,8 +7,10 @@
 
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -18,7 +20,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
@@ -30,7 +31,8 @@
 import com.android.quickstep.util.MultiValueUpdateListener;
 
 /**
- * Create an instance via {@link #getFloatingTaskView(StatefulActivity, TaskView, RectF)} to
+ * Create an instance via
+ * {@link #getFloatingTaskView(StatefulActivity, View, Bitmap, Drawable, RectF)} to
  * which will have the thumbnail from the provided existing TaskView overlaying the taskview itself.
  *
  * Can then animate the taskview using
@@ -44,7 +46,7 @@
 
     private SplitPlaceholderView mSplitPlaceholderView;
     private RectF mStartingPosition;
-    private final BaseDraggingActivity mActivity;
+    private final StatefulActivity mActivity;
     private final boolean mIsRtl;
     private final Rect mOutline = new Rect();
     private PagedOrientationHandler mOrientationHandler;
@@ -74,7 +76,8 @@
         mSplitPlaceholderView.setAlpha(0);
     }
 
-    private void init(StatefulActivity launcher, TaskView originalView, RectF positionOut) {
+    private void init(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail,
+            Drawable icon, RectF positionOut) {
         mStartingPosition = positionOut;
         updateInitialPositionForView(originalView);
         final InsettableFrameLayout.LayoutParams lp =
@@ -86,12 +89,12 @@
         setPivotY(0);
 
         // Copy bounds of exiting thumbnail into ImageView
-        TaskThumbnailView thumbnail = originalView.getThumbnail();
-        mImageView.setImageBitmap(thumbnail.getThumbnail());
+        mImageView.setImageBitmap(thumbnail);
         mImageView.setVisibility(VISIBLE);
 
-        mOrientationHandler = originalView.getRecentsView().getPagedOrientationHandler();
-        mSplitPlaceholderView.setIconView(originalView.getIconView(),
+        RecentsView recentsView = launcher.getOverviewPanel();
+        mOrientationHandler = recentsView.getPagedOrientationHandler();
+        mSplitPlaceholderView.setIcon(icon,
                 launcher.getDeviceProfile().overviewTaskIconDrawableSizePx);
         mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
     }
@@ -101,21 +104,20 @@
      * appearance of {@code originalView}.
      */
     public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
-            TaskView originalView, RectF positionOut) {
+            View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) {
         final BaseDragLayer dragLayer = launcher.getDragLayer();
         ViewGroup parent = (ViewGroup) dragLayer.getParent();
         final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater()
                 .inflate(R.layout.floating_split_select_view, parent, false);
 
-        floatingView.init(launcher, originalView, positionOut);
+        floatingView.init(launcher, originalView, thumbnail, icon, positionOut);
         parent.addView(floatingView);
         return floatingView;
     }
 
-    public void updateInitialPositionForView(TaskView originalView) {
-        View thumbnail = originalView.getThumbnail();
-        Rect viewBounds = new Rect(0, 0, thumbnail.getWidth(), thumbnail.getHeight());
-        Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), thumbnail, viewBounds,
+    public void updateInitialPositionForView(View originalView) {
+        Rect viewBounds = new Rect(0, 0, originalView.getWidth(), originalView.getHeight());
+        Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), originalView, viewBounds,
                 true /* ignoreTransform */, null /* recycle */,
                 mStartingPosition);
         mStartingPosition.offset(originalView.getTranslationX(), originalView.getTranslationY());
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 718effe..3ddec26 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -180,8 +180,8 @@
 
     @Override
     public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
-        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
-                STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+        getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, null,
+                mSecondaryTask.key.id, STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
                 getSplitRatio());
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index e0395ea..e8f3324 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -168,4 +169,10 @@
         super.initiateSplitSelect(taskView, stagePosition);
         mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
     }
+
+    @Override
+    public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
+        super.initiateSplitSelect(splitSelectSource);
+        mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index fcc6272..6a3286b 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -214,12 +214,13 @@
                 setPadding(mInsets.left, 0, mInsets.right + additionalPadding, 0);
             }
 
-            // Align vertically, using taskbar height + mDp.taskbarOffsetY() to guestimate
-            // where the button nav top is
+            // Align vertically, using taskbar height + mDp.taskbarOffsetY() to estimate where
+            // the button nav top is.
             View startActionView = findViewById(R.id.action_screenshot);
             int marginBottom = getOverviewActionsBottomMarginPx(
                     SysUINavigationMode.getMode(getContext()), mDp);
-            int actionsTop = (mDp.heightPx - marginBottom - mInsets.bottom);
+            int actionsTop =
+                    (mDp.heightPx - marginBottom - mInsets.bottom) - startActionView.getHeight();
             int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY());
             int transY = navTop - actionsTop
                     + ((mDp.taskbarSize - startActionView.getHeight()) / 2);
@@ -296,10 +297,13 @@
             return inset;
         }
 
+        // Actions button will be aligned with nav buttons in updatePaddingAndTranslations().
         if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) {
             return dp.overviewActionsMarginThreeButtonPx + inset;
         }
 
-        return dp.overviewActionsBottomMarginGesturePx + inset;
+        // There is no bottom inset when taskbar is present, use stashed taskbar as padding instead.
+        return dp.overviewActionsBottomMarginGesturePx
+                + (dp.isTaskbarPresent ? dp.stashedTaskbarSize : inset);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index aadf67c..1d14bad 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -16,6 +16,7 @@
 
 package com.android.quickstep.views;
 
+import static android.app.PendingIntent.FLAG_MUTABLE;
 import static android.view.Surface.ROTATION_0;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
@@ -27,6 +28,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.Utilities.squaredHypot;
@@ -67,6 +69,8 @@
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.LocusId;
 import android.content.res.Configuration;
@@ -128,6 +132,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -622,6 +627,9 @@
     private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
             R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
 
+    @Nullable
+    private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource;
+
     /**
      * 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
@@ -2001,6 +2009,22 @@
         return null;
     }
 
+    @Nullable
+    private TaskView getTaskViewByComponentName(ComponentName componentName) {
+        if (componentName == null) {
+            return null;
+        }
+
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = requireTaskViewAt(i);
+            if (taskView.getItemInfo().getIntent().getComponent().getPackageName().equals(
+                    componentName.getPackageName())) {
+                return taskView;
+            }
+        }
+        return null;
+    }
+
     public int getRunningTaskIndex() {
         TaskView taskView = getRunningTaskView();
         return taskView == null ? -1 : indexOfChild(taskView);
@@ -2696,12 +2720,23 @@
                 mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
 
         RectF startingTaskRect = new RectF();
-        mSplitHiddenTaskView.setVisibility(INVISIBLE);
-        mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
-                mSplitHiddenTaskView, startingTaskRect);
-        mFirstFloatingTaskView.setAlpha(1);
-        mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
-                mTempRect, mSplitHiddenTaskView, true /*fadeWithThumbnail*/);
+        if (mSplitHiddenTaskView != null) {
+            mSplitHiddenTaskView.setVisibility(INVISIBLE);
+            mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+                    mSplitHiddenTaskView, mSplitHiddenTaskView.getThumbnail().getThumbnail(),
+                    mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
+            mFirstFloatingTaskView.setAlpha(1);
+            mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
+                    mTempRect, mSplitHiddenTaskView, true /*fadeWithThumbnail*/);
+        } else {
+            mSplitSelectSource.view.setVisibility(INVISIBLE);
+            mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+                    mSplitSelectSource.view, null,
+                    mSplitSelectSource.drawable, startingTaskRect);
+            mFirstFloatingTaskView.setAlpha(1);
+            mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
+                    mTempRect, mSplitSelectSource.view, true /*fadeWithThumbnail*/);
+        }
         anim.addEndListener(success -> {
             if (success) {
                 mSplitToast.show();
@@ -3924,20 +3959,40 @@
 
     public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition) {
         mSplitHiddenTaskView = taskView;
-        Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
-                taskView.getBottom());
-        mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
-                stagePosition, initialBounds);
+        mSplitSelectStateController.setInitialTaskSelect(taskView.getTask().key.id,
+                stagePosition);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             finishRecentsAnimation(true, null);
         }
     }
 
+    public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
+        // Remove the task if it exists in Overview
+        TaskView matchingTaskView = getTaskViewByComponentName(
+                splitSelectSource.intent.getComponent());
+        if (matchingTaskView != null) {
+            removeTaskInternal(matchingTaskView.getTaskViewId());
+        }
+
+        mSplitSelectSource = splitSelectSource;
+        mSplitSelectStateController.setInitialTaskSelect(
+                PendingIntent.getActivity(
+                        mContext, 0, splitSelectSource.intent, FLAG_MUTABLE),
+                splitSelectSource.position.stagePosition);
+    }
+
     public PendingAnimation createSplitSelectInitAnimation() {
-        int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
-        return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
-                true /* dismissingForSplitSelection*/);
+        if (mSplitHiddenTaskView != null) {
+            int duration = mActivity.getStateManager().getState().getTransitionDuration(
+                    getContext());
+            return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
+                    true /* dismissingForSplitSelection*/);
+        } else {
+            PendingAnimation anim = new PendingAnimation(SPLIT_LAUNCH_DURATION);
+            createInitialSplitSelectAnimation(anim);
+            return anim;
+        }
     }
 
     public void confirmSplitSelect(TaskView taskView) {
@@ -3968,13 +4023,14 @@
                 false /*fadeWithThumbnail*/);
 
         mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
-                taskView, secondTaskStartingBounds);
+                taskView, taskView.getThumbnail().getThumbnail(),
+                taskView.getIconView().getDrawable(), secondTaskStartingBounds);
         mSecondFloatingTaskView.setAlpha(1);
         mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
                 secondTaskEndingBounds, taskView.getThumbnail(),
                 true /*fadeWithThumbnail*/);
         pendingAnimation.addEndListener(aBoolean ->
-                mSplitSelectStateController.setSecondTaskId(taskView.getTask(),
+                mSplitSelectStateController.setSecondTaskId(taskView.getTask().key.id,
                 aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
         mSecondSplitHiddenTaskView = taskView;
         taskView.setVisibility(INVISIBLE);
@@ -3983,6 +4039,20 @@
 
     /** TODO(b/181707736) More gracefully handle exiting split selection state */
     protected void resetFromSplitSelectionState() {
+        if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
+            if (mFirstFloatingTaskView != null) {
+                mActivity.getRootView().removeView(mFirstFloatingTaskView);
+                mFirstFloatingTaskView = null;
+            }
+            if (mSecondFloatingTaskView != null) {
+                mActivity.getRootView().removeView(mSecondFloatingTaskView);
+                mSecondFloatingTaskView = null;
+                mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
+                mSecondSplitHiddenTaskView = null;
+            }
+            mSplitSelectSource = null;
+        }
+
         if (mSplitHiddenTaskViewIndex == -1) {
             return;
         }
@@ -4002,16 +4072,6 @@
             mSplitHiddenTaskView.setVisibility(VISIBLE);
             mSplitHiddenTaskView = null;
         }
-        if (mFirstFloatingTaskView != null) {
-            mActivity.getRootView().removeView(mFirstFloatingTaskView);
-            mFirstFloatingTaskView = null;
-        }
-        if (mSecondFloatingTaskView != null) {
-            mActivity.getRootView().removeView(mSecondFloatingTaskView);
-            mSecondFloatingTaskView = null;
-            mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
-            mSecondSplitHiddenTaskView = null;
-        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index 04a5761..cfa482f 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -17,6 +17,7 @@
 package com.android.quickstep.views;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.Gravity;
@@ -52,14 +53,14 @@
         return mIconView;
     }
 
-    public void setIconView(IconView iconView, int iconSize) {
+    public void setIcon(Drawable drawable, int iconSize) {
         if (mIconView == null) {
             mIconView = new IconView(getContext());
             addView(mIconView);
         }
-        mIconView.setDrawable(iconView.getDrawable());
+        mIconView.setDrawable(drawable);
         mIconView.setDrawableSize(iconSize, iconSize);
-        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(iconView.getLayoutParams());
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(iconSize, iconSize);
         params.gravity = Gravity.CENTER;
         mIconView.setLayoutParams(params);
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index b4917f3..e3198a8 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Intent;
 
@@ -37,7 +38,6 @@
 import com.android.launcher3.tapl.Overview;
 import com.android.launcher3.tapl.OverviewActions;
 import com.android.launcher3.tapl.OverviewTask;
-import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -322,10 +322,8 @@
     @Test
     @PortraitLandscape
     public void testOverviewForTablet() throws Exception {
-        // TODO(b/210158657): Re-enable for OOP
-        if (!mLauncher.isTablet() || !TestHelpers.isInLauncherProcess()) {
-            return;
-        }
+        assumeTrue(mLauncher.isTablet());
+
         for (int i = 2; i <= 14; i++) {
             startTestActivity(i);
         }
diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml
new file mode 100644
index 0000000..52b919b
--- /dev/null
+++ b/res/drawable/ic_all_apps_button.xml
@@ -0,0 +1,43 @@
+<?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="80dp"
+    android:height="80dp"
+    android:viewportWidth="80"
+    android:viewportHeight="80">
+  <path
+      android:pathData="M40,0.5L40,0.5c21.8,0 39.5,17.7 39.5,39.5l0,0c0,21.8 -17.7,39.5 -39.5,39.5l0,0C18.2,79.5 0.5,61.8 0.5,40l0,0C0.5,18.2 18.2,0.5 40,0.5z"
+      android:fillColor="#F7F9FA"/>
+  <path
+      android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+      android:fillColor="#00677E"/>
+  <path
+      android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+      android:fillColor="#5F757E"/>
+  <path
+      android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+      android:fillColor="#5F757E"/>
+  <path
+      android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+      android:fillColor="#6C6F93"/>
+  <path
+      android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+      android:fillColor="#005A6E"/>
+  <path
+      android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
+      android:fillColor="#5F757E"/>
+</vector>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a6c5ed8..ddc7d10 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -324,6 +324,7 @@
 
 <!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="taskbar_size">0dp</dimen>
+    <dimen name="taskbar_stashed_size">0dp</dimen>
     <dimen name="qsb_widget_height">0dp</dimen>
     <dimen name="taskbar_icon_size">44dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 1a5b321..c456a4d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -219,6 +219,7 @@
     // Whether Taskbar will inset the bottom of apps by taskbarSize.
     public boolean isTaskbarPresentInApps;
     public int taskbarSize;
+    public int stashedTaskbarSize;
 
     // DragController
     public int flingToDeleteThresholdVelocity;
@@ -256,12 +257,7 @@
         widthPx = windowBounds.bounds.width();
         heightPx = windowBounds.bounds.height();
         availableWidthPx = windowBounds.availableSize.x;
-        int taskbarInset = isTaskbarPresent
-                ? ResourceUtils.getNavbarSize(
-                        isLandscape ? "navigation_bar_height_landscape" : "navigation_bar_height",
-                        res)
-                : 0;
-        availableHeightPx =  windowBounds.availableSize.y - taskbarInset;
+        availableHeightPx =  windowBounds.availableSize.y;
 
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@@ -283,6 +279,7 @@
 
         if (isTaskbarPresent) {
             taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
+            stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
         }
 
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
@@ -1154,6 +1151,7 @@
         writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
                 hotseatBarSidePaddingEndPx));
         writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
+        writer.println(prefix + pxToDpStr("hotseatBorderSpace", hotseatBorderSpace));
         writer.println(prefix + "\tisQsbInline: " + isQsbInline);
         writer.println(prefix + pxToDpStr("qsbWidth", qsbWidth));
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 0b168a5..a381787 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -135,7 +135,7 @@
     /**
      * Number of icons inside the hotseat area.
      */
-    protected int numShownHotseatIcons;
+    public int numShownHotseatIcons;
 
     /**
      * Number of icons inside the hotseat area that is stored in the database. This is greater than
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 59c40cc..fb4a7fd 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -41,6 +41,7 @@
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
+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;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY;
@@ -141,6 +142,8 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
@@ -1103,14 +1106,23 @@
                 && mAllAppsSessionLogId == null) {
             // creates new instance ID since new all apps session is started.
             mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId();
-            getStatsLogManager()
-                    .logger()
-                    .log(FeatureFlags.ENABLE_DEVICE_SEARCH.get()
-                            ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
-                            : LAUNCHER_ALLAPPS_ENTRY);
+            getStatsLogManager().logger().withContainerInfo(
+                    ContainerInfo.newBuilder().setWorkspace(
+                            WorkspaceContainer.newBuilder().setPageIndex(
+                                    getWorkspace().getCurrentPage())).build())
+                    .log(getAllAppsEntryEvent());
         }
     }
 
+    /**
+     * Returns {@link EventEnum} that should be logged when Launcher enters into AllApps state.
+     */
+    protected EventEnum getAllAppsEntryEvent() {
+        return FeatureFlags.ENABLE_DEVICE_SEARCH.get()
+                ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
+                : LAUNCHER_ALLAPPS_ENTRY;
+    }
+
     @Override
     public void onStateSetEnd(LauncherState state) {
         super.onStateSetEnd(state);
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 5ef3690..e3aa758 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -105,8 +105,9 @@
                     resources,
                     INVALID_RESOURCE_HANDLE) == 2;
             if (dp.isTablet || isGesturalMode) {
-                newNavInsets.bottom = ResourceUtils.getNavbarSize(
-                        "navigation_bar_height_landscape", resources);
+                newNavInsets.bottom = dp.isTaskbarPresent
+                        ? 0
+                        : ResourceUtils.getNavbarSize("navigation_bar_height_landscape", resources);
             } else {
                 int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
                 if (dp.isSeascape()) {
@@ -116,7 +117,9 @@
                 }
             }
         } else {
-            newNavInsets.bottom = ResourceUtils.getNavbarSize("navigation_bar_height", resources);
+            newNavInsets.bottom = dp.isTaskbarPresent
+                    ? 0
+                    : ResourceUtils.getNavbarSize("navigation_bar_height", resources);
         }
         updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
         updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f765de4..5bc10fb 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -249,9 +249,13 @@
             "Enables scaling/spacing for icon labels to make more characters visible");
 
     public static final BooleanFlag ENABLE_ALL_APPS_IN_TASKBAR = getDebugFlag(
-            "ENABLE_ALL_APPS_IN_TASKBAR", false,
+            "ENABLE_ALL_APPS_IN_TASKBAR", true,
             "Enables accessing All Apps from the system Taskbar.");
 
+    public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(
+            "ENABLE_SPLIT_FROM_WORKSPACE", false,
+            "Enable initiating split screen from workspace.");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index a7cfdde..0d2bc37 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -258,6 +258,9 @@
         @UiEvent(doc = "User swipes or fling in DOWN direction to close apps drawer.")
         LAUNCHER_ALLAPPS_CLOSE_DOWN(569),
 
+        @UiEvent(doc = "User tap outside apps drawer sheet to close apps drawer.")
+        LAUNCHER_ALLAPPS_CLOSE_TAP_OUTSIDE(941),
+
         @UiEvent(doc = "User swipes or fling in UP direction and hold from the bottom bazel area")
         LAUNCHER_OVERVIEW_GESTURE(570),
 
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 93387ad..e8941e6 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -23,6 +23,7 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_TAP_OUTSIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_LONGPRESS;
 
 import android.graphics.PointF;
@@ -40,6 +41,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 
@@ -155,6 +157,16 @@
                 && mLauncher.isInState(ALL_APPS)
                 && mLauncher.getDeviceProfile().isTablet) {
             mLauncher.getStateManager().goToState(NORMAL);
+            mLauncher.getStatsLogManager().logger()
+                    .withSrcState(ALL_APPS.statsLogOrdinal)
+                    .withDstState(NORMAL.statsLogOrdinal)
+                    .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                            .setWorkspace(
+                                    LauncherAtom.WorkspaceContainer.newBuilder()
+                                            .setPageIndex(
+                                                    mLauncher.getWorkspace().getCurrentPage()))
+                            .build())
+                    .log(LAUNCHER_ALLAPPS_CLOSE_TAP_OUTSIDE);
         }
 
         return result;
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
new file mode 100644
index 0000000..f502d46
--- /dev/null
+++ b/src/com/android/launcher3/views/AllAppsButton.java
@@ -0,0 +1,47 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.FastBitmapDrawable;
+
+/**
+ * Button in Taskbar that opens All Apps.
+ */
+public class AllAppsButton extends BubbleTextView {
+
+    public AllAppsButton(Context context) {
+        this(context, null);
+    }
+
+    public AllAppsButton(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AllAppsButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
+                .createScaledBitmapWithShadow(context.getDrawable(R.drawable.ic_all_apps_button));
+        setIcon(new FastBitmapDrawable(bitmap));
+    }
+}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index f0b4ba0..53c772f 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,12 +16,16 @@
 
 package com.android.launcher3.widget;
 
+import android.annotation.TargetApi;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
@@ -52,6 +56,8 @@
         implements TouchCompleteListener, View.OnLongClickListener,
         LocalColorExtractor.Listener {
 
+    private static final String TAG = "LauncherAppWidgetHostView";
+
     // Related to the auto-advancing of widgets
     private static final long ADVANCE_INTERVAL = 20000;
     private static final long ADVANCE_STAGGER = 250;
@@ -61,6 +67,8 @@
     // Maximum duration for which updates can be deferred.
     private static final long UPDATE_LOCK_TIMEOUT_MILLIS = 1000;
 
+    private static final String TRACE_METHOD_NAME = "appwidget load-widget ";
+
     private final Rect mTempRect = new Rect();
     private final CheckLongPressHelper mLongPressHelper;
     protected final Launcher mLauncher;
@@ -88,6 +96,8 @@
     /** The drag content height which is only set when the drag content scale is not 1f. */
     private int mDragContentHeight = 0;
 
+    private boolean mTrackingWidgetUpdate = false;
+
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mLauncher = Launcher.getLauncher(context);
@@ -121,7 +131,25 @@
     }
 
     @Override
+    @TargetApi(Build.VERSION_CODES.Q)
+    public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
+        super.setAppWidget(appWidgetId, info);
+        if (!mTrackingWidgetUpdate && Utilities.ATLEAST_Q) {
+            mTrackingWidgetUpdate = true;
+            Trace.beginAsyncSection(TRACE_METHOD_NAME + info.provider, appWidgetId);
+            Log.i(TAG, "App widget created with id: " + appWidgetId);
+        }
+    }
+
+    @Override
+    @TargetApi(Build.VERSION_CODES.Q)
     public void updateAppWidget(RemoteViews remoteViews) {
+        if (mTrackingWidgetUpdate && remoteViews != null && Utilities.ATLEAST_Q) {
+            Log.i(TAG, "App widget with id: " + getAppWidgetId() + " loaded");
+            Trace.endAsyncSection(
+                    TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId());
+            mTrackingWidgetUpdate = false;
+        }
         if (isDeferringUpdates()) {
             mDeferredRemoteViews = remoteViews;
             return;
diff --git a/tests/Android.bp b/tests/Android.bp
index f3e0500..c2c545b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -11,13 +11,10 @@
 // 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 {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "packages_apps_Launcher3_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["packages_apps_Launcher3_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 // Source code used for test
@@ -46,6 +43,7 @@
       "src/com/android/launcher3/util/rule/TestStabilityRule.java",
       "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
       "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
+      "src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
       "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
       "src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
       "src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
diff --git a/tests/dummy_app/Android.bp b/tests/dummy_app/Android.bp
index 4f83bb7..08ce2f7 100644
--- a/tests/dummy_app/Android.bp
+++ b/tests/dummy_app/Android.bp
@@ -15,7 +15,8 @@
 //
 
 package {
-    default_applicable_licenses:   ["packages_apps_Launcher3_license"],
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 android_app {