Merge "Give FloatingIconView a copy of, rather than, the original icon." into tm-dev am: 0c809bbe67 am: 9dd7aa50ab

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/18673699

Change-Id: If43a7b8fa1722abdc7d1c49a15cca1d7d5189bd4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 0a55675..4adbf53 100644
--- a/Android.bp
+++ b/Android.bp
@@ -313,3 +313,138 @@
         baseline_filename: "lint-baseline-launcher3.xml",
     },
 }
+
+// Build rule for Launcher3 Go app for Android Go devices.
+android_app {
+    name: "Launcher3Go",
+
+    static_libs: ["Launcher3CommonDepsLib"],
+
+    srcs: [
+        "src/**/*.java",
+        "src_ui_overrides/**/*.java",
+        "go/src/**/*.java",
+    ],
+
+    resource_dirs: ["go/res"],
+
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+
+    sdk_version: "current",
+    min_sdk_version: "current",
+    target_sdk_version: "current",
+    privileged: true,
+    system_ext_specific: true,
+    overrides: [
+        "Home",
+        "Launcher2",
+        "Launcher3",
+        "Launcher3QuickStep",
+    ],
+    required: ["privapp_whitelist_com.android.launcher3"],
+
+    additional_manifests: [
+        "AndroidManifest.xml",
+        "AndroidManifest-common.xml",
+    ],
+
+    manifest: "go/AndroidManifest.xml",
+    jacoco: {
+        include_filter: ["com.android.launcher3.*"],
+    }
+
+}
+
+// Build rule for Quickstep app.
+android_app {
+    name: "Launcher3QuickStep",
+
+    static_libs: ["Launcher3QuickStepLib"],
+    optimize: {
+        enabled: false,
+    },
+
+    platform_apis: true,
+    min_sdk_version: "current",
+    target_sdk_version: "current",
+
+    privileged: true,
+    system_ext_specific: true,
+    overrides: [
+        "Home",
+        "Launcher2",
+        "Launcher3",
+    ],
+    required: ["privapp_whitelist_com.android.launcher3"],
+
+    resource_dirs: ["quickstep/res"],
+
+    additional_manifests: [
+        "quickstep/AndroidManifest-launcher.xml",
+        "AndroidManifest-common.xml",
+    ],
+
+    manifest: "quickstep/AndroidManifest.xml",
+    jacoco: {
+        include_filter: ["com.android.launcher3.*"],
+    }
+
+}
+
+// Build rule for Launcher3 Go app with quickstep for Android Go devices.
+android_app {
+    name: "Launcher3QuickStepGo",
+
+    static_libs: [
+        "SystemUI-statsd",
+        "SystemUISharedLib",
+        "LauncherGoResLib",
+    ],
+
+    platform_apis: true,
+    min_sdk_version: "current",
+    target_sdk_version: "current",
+
+    srcs: [
+        "src/**/*.java",
+        "quickstep/src/**/*.java",
+        "go/src/**/*.java",
+        "go/quickstep/src/**/*.java",
+    ],
+
+    resource_dirs: [
+        "go/quickstep/res",
+        "go/res",
+        "quickstep/res",
+    ],
+
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+        enabled: true,
+    },
+
+    privileged: true,
+    system_ext_specific: true,
+    overrides: [
+        "Home",
+        "Launcher2",
+        "Launcher3",
+        "Launcher3QuickStep",
+    ],
+    required: ["privapp_whitelist_com.android.launcher3"],
+
+    additional_manifests: [
+        "go/AndroidManifest.xml",
+        "go/AndroidManifest-launcher.xml",
+        "AndroidManifest-common.xml",
+    ],
+
+    manifest: "quickstep/AndroidManifest.xml",
+    jacoco: {
+        include_filter: ["com.android.launcher3.*"],
+    }
+
+}
+
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 1bc8b28..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-#
-# Build rule for Launcher3 Go app for Android Go devices.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, src_ui_overrides) \
-    $(call all-java-files-under, go/src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/go/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 26
-LOCAL_PACKAGE_NAME := Launcher3Go
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := \
-    $(LOCAL_PATH)/AndroidManifest.xml \
-    $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_MANIFEST_FILE := go/AndroidManifest.xml
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-include $(BUILD_PACKAGE)
-
-#
-# Build rule for Quickstep app.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3QuickStepLib
-LOCAL_PROGUARD_ENABLED := disabled
-
-ifneq (,$(wildcard frameworks/base))
-  LOCAL_PRIVATE_PLATFORM_APIS := true
-else
-  LOCAL_SDK_VERSION := system_current
-  LOCAL_MIN_SDK_VERSION := 26
-endif
-LOCAL_PACKAGE_NAME := Launcher3QuickStep
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := \
-    $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
-    $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-include $(BUILD_PACKAGE)
-
-
-#
-# Build rule for Launcher3 Go app with quickstep for Android Go devices.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    SystemUI-statsd \
-    SystemUISharedLib
-ifneq (,$(wildcard frameworks/base))
-  LOCAL_PRIVATE_PLATFORM_APIS := true
-else
-  LOCAL_SDK_VERSION := system_current
-  LOCAL_MIN_SDK_VERSION := 26
-endif
-LOCAL_STATIC_ANDROID_LIBRARIES := LauncherGoResLib
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, go/src) \
-    $(call all-java-files-under, go/quickstep/src)
-
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/go/quickstep/res \
-    $(LOCAL_PATH)/go/res \
-    $(LOCAL_PATH)/quickstep/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-LOCAL_PROGUARD_ENABLED := full
-
-LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := \
-    $(LOCAL_PATH)/go/AndroidManifest.xml \
-    $(LOCAL_PATH)/go/AndroidManifest-launcher.xml \
-    $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-include $(BUILD_PACKAGE)
-
-
-# ==================================================
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/quickstep/res/drawable/split_instructions_background.xml b/quickstep/res/drawable/split_instructions_background.xml
new file mode 100644
index 0000000..6d0e7db
--- /dev/null
+++ b/quickstep/res/drawable/split_instructions_background.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorAccentPrimary" />
+    <corners android:radius="@dimen/split_instructions_radius" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/split_instructions_view.xml b/quickstep/res/layout/split_instructions_view.xml
new file mode 100644
index 0000000..91fb05c
--- /dev/null
+++ b/quickstep/res/layout/split_instructions_view.xml
@@ -0,0 +1,35 @@
+<?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.quickstep.views.SplitInstructionsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@drawable/split_instructions_background"
+    android:paddingRight="@dimen/split_instructions_horizontal_padding"
+    android:paddingLeft="@dimen/split_instructions_horizontal_padding"
+    android:paddingTop="@dimen/split_instructions_vertical_padding"
+    android:paddingBottom="@dimen/split_instructions_vertical_padding"
+    android:elevation="@dimen/split_instructions_elevation"
+    android:visibility="gone">
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/split_instructions_text"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:gravity="center"
+        android:textColor="?androidprv:attr/textColorOnAccent"
+        android:text="@string/toast_split_select_app" />
+</com.android.quickstep.views.SplitInstructionsView>
\ No newline at end of file
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index f233bde..732f511 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -15,7 +15,8 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="overview_task_margin">8dp</dimen>
+    <!--  Overview actions  -->
+    <dimen name="overview_actions_top_margin">12dp</dimen>
 
     <!-- Tips Gesture Tutorial -->
     <dimen name="gesture_tutorial_feedback_margin_start_end">126dp</dimen>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3072a3e..0fd3c4a 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -239,7 +239,7 @@
     <dimen name="navigation_key_padding">0dp</dimen>
 
     <!-- Floating rotation button -->
-    <dimen name="floating_rotation_button_diameter">40dp</dimen>
+    <dimen name="floating_rotation_button_diameter">52dp</dimen>
     <dimen name="floating_rotation_button_min_margin">20dp</dimen>
     <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
     <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2239102..95d6dd0 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -92,6 +92,9 @@
 import com.android.systemui.unfold.UnfoldTransitionFactory;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
+import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -343,15 +346,17 @@
     }
 
     private void initUnfoldTransitionProgressProvider() {
-        final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
+        final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
         if (config.isEnabled()) {
             mUnfoldTransitionProgressProvider =
                     UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
-                            this,
+                            /* context= */ this,
                             config,
                             ProxyScreenStatusProvider.INSTANCE,
-                            getSystemService(DeviceStateManager.class),
-                            getSystemService(ActivityManager.class),
+                            new DeviceStateManagerFoldProvider(
+                                    getSystemService(DeviceStateManager.class), /* context */this),
+                            new ActivityManagerActivityTypeProvider(
+                                    getSystemService(ActivityManager.class)),
                             getSystemService(SensorManager.class),
                             getMainThreadHandler(),
                             getMainExecutor(),
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index ee5bb44..99f7bdd 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -23,16 +23,15 @@
 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
 import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
 import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
-import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Matrix;
@@ -161,11 +160,7 @@
         if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
             gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo);
         }
-        try {
-            mContext.startActivity(intent, options.toBundle());
-        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
-            mContext.startActivity(createHomeIntent());
-        }
+        startHomeIntentSafely(mContext, intent, options.toBundle());
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0efe666..9e3173c 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,11 +20,11 @@
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 
-import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
 
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -33,8 +33,12 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Bundle;
 import android.util.SparseIntArray;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.tracing.OverviewComponentObserverProto;
 import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -276,4 +280,34 @@
         overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
         serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
     }
+
+    /**
+     * Starts the intent for the current home activity.
+     */
+    public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options) {
+        RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context);
+        OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState);
+        Intent intent = observer.getHomeIntent();
+        observer.onDestroy();
+        deviceState.destroy();
+        startHomeIntentSafely(context, intent, options);
+    }
+
+    /**
+     * Starts the intent for the current home activity.
+     */
+    public static void startHomeIntentSafely(
+            @NonNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options) {
+        try {
+            context.startActivity(homeIntent, options);
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+            context.startActivity(createHomeIntent(), options);
+        }
+    }
+
+    private static Intent createHomeIntent() {
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 4f0b976..67ce606 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -21,11 +21,11 @@
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -428,7 +428,7 @@
         RemoteAnimationAdapterCompat adapterCompat =
                 new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
                         getIApplicationThread());
-        startActivity(createHomeIntent(),
+        startHomeIntentSafely(this,
                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
     }
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 16f141b..76513a4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -51,6 +51,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.InputEvent;
@@ -127,6 +128,9 @@
 
     private static final String TAG = "TouchInteractionService";
 
+    private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
+            SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", false);
+
     private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
     private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
     private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
@@ -708,16 +712,30 @@
                 base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
             }
 
-            // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
-            // instead of going all the way home when a swipe up is detected.
-            // Notification panel can be expanded on top of expanded bubbles. Bubbles remain
-            // expanded in the back. Make sure swipe up is not passed to bubbles in this case.
-            if ((mDeviceState.isBubblesExpanded() && !mDeviceState.isNotificationPanelExpanded())
-                    || mDeviceState.isSystemUiDialogShowing()) {
+            if (mDeviceState.isBubblesExpanded()) {
+                if (BUBBLES_HOME_GESTURE_ENABLED) {
+                    // Bubbles can handle home gesture itself.
+                    base = getDefaultInputConsumer();
+                } else {
+                    // If Bubbles is expanded, use the overlay input consumer, which will close
+                    // Bubbles instead of going all the way home when a swipe up is detected.
+                    // Notification panel can be expanded on top of expanded bubbles. Bubbles remain
+                    // expanded in the back. Make sure swipe up is not passed to bubbles in this
+                    // case.
+                    if (!mDeviceState.isNotificationPanelExpanded()) {
+                        base = new SysUiOverlayInputConsumer(
+                                getBaseContext(), mDeviceState, mInputMonitorCompat);
+                    }
+                }
+            }
+
+            if (mDeviceState.isSystemUiDialogShowing()) {
                 base = new SysUiOverlayInputConsumer(
                         getBaseContext(), mDeviceState, mInputMonitorCompat);
             }
 
+
+
             if (mDeviceState.isScreenPinningActive()) {
                 // Note: we only allow accessibility to wrap this, and it replaces the previous
                 // base input consumer (which should be NO_OP anyway since topTaskLocked == true).
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 3d737ca..c1750b5 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -19,12 +19,13 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
 import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
 import android.animation.Animator;
@@ -101,6 +102,8 @@
 
     private boolean mThresholdCrossed = false;
     private boolean mHomeLaunched = false;
+    private boolean mCancelWhenRecentsStart = false;
+    private boolean mDismissTask = false;
 
     private RecentsAnimationController mRecentsAnimationController;
 
@@ -204,9 +207,20 @@
             animator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    if (dismissTask) {
-                        // For now, just start the home intent so user is prompted to unlock the device.
-                        mContext.startActivity(createHomeIntent());
+                    if (ENABLE_SHELL_TRANSITIONS) {
+                        if (mTaskAnimationManager.getCurrentCallbacks() != null) {
+                            if (mRecentsAnimationController != null) {
+                                finishRecentsAnimationForShell(dismissTask);
+                            } else {
+                                // the transition of recents animation hasn't started, wait for it
+                                mCancelWhenRecentsStart = true;
+                                mDismissTask = dismissTask;
+                            }
+                        }
+                    } else if (dismissTask) {
+                        // For now, just start the home intent so user is prompted to
+                        // unlock the device.
+                        startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
                         mHomeLaunched = true;
                     }
                     mStateCallback.setState(STATE_HANDLER_INVALIDATED);
@@ -238,12 +252,24 @@
         mTransformParams.setTargetSet(targets);
         applyTransform();
         mStateCallback.setState(STATE_TARGET_RECEIVED);
+        if (mCancelWhenRecentsStart) {
+            finishRecentsAnimationForShell(mDismissTask);
+        }
     }
 
     @Override
     public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
         mRecentsAnimationController = null;
         mTransformParams.setTargetSet(null);
+        mCancelWhenRecentsStart = false;
+    }
+
+    private void finishRecentsAnimationForShell(boolean dismissTask) {
+        mCancelWhenRecentsStart = false;
+        mTaskAnimationManager.finishRunningRecentsAnimation(dismissTask /* toHome */);
+        if (dismissTask) {
+            mHomeLaunched = true;
+        }
     }
 
     private void endRemoteAnimation() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 11f0ff3..10b4ff9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -47,6 +48,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.tracing.InputConsumerProto;
@@ -58,7 +60,9 @@
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.RotationTouchHelper;
 import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.util.ActiveGestureLog;
@@ -107,6 +111,7 @@
     private VelocityTracker mVelocityTracker;
 
     private AbsSwipeUpHandler mInteractionHandler;
+    private final FinishImmediatelyHandler mCleanupHandler = new FinishImmediatelyHandler();
 
     private final boolean mIsDeferredDownTarget;
     private final PointF mDownPos = new PointF();
@@ -377,6 +382,7 @@
 
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
             mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
+            mActiveCallbacks.removeListener(mCleanupHandler);
             mActiveCallbacks.addListener(mInteractionHandler);
             mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
             notifyGestureStarted(true /*isLikelyToStartNewTask*/);
@@ -414,7 +420,19 @@
             }
         } else {
             // Since we start touch tracking on DOWN, we may reach this state without actually
-            // starting the gesture. In that case, just cleanup immediately.
+            // starting the gesture. In that case, we need to clean-up an unfinished or un-started
+            // animation.
+            if (mActiveCallbacks != null && mInteractionHandler != null) {
+                if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+                    // The animation started, but with no movement, in this case, there will be no
+                    // animateToProgress so we have to manually finish here.
+                    mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+                } else {
+                    // The animation hasn't started yet, so insert a replacement handler into the
+                    // callbacks which immediately finishes the animation after it starts.
+                    mActiveCallbacks.addListener(mCleanupHandler);
+                }
+            }
             onConsumerAboutToBeSwitched();
             onInteractionGestureFinished();
 
@@ -457,7 +475,7 @@
     }
 
     private void removeListener() {
-        if (mActiveCallbacks != null) {
+        if (mActiveCallbacks != null && mInteractionHandler != null) {
             mActiveCallbacks.removeListener(mInteractionHandler);
         }
     }
@@ -483,4 +501,19 @@
             mInteractionHandler.writeToProto(inputConsumerProto);
         }
     }
+
+    /**
+     * A listener which just finishes the animation immediately after starting. Replaces
+     * AbsSwipeUpHandler if the gesture itself finishes before the animation even starts.
+     */
+    private static class FinishImmediatelyHandler
+            implements RecentsAnimationCallbacks.RecentsAnimationListener {
+
+        public void onRecentsAnimationStart(RecentsAnimationController controller,
+                RecentsAnimationTargets targets) {
+            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+                controller.finish(false /* toRecents */, null);
+            });
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 864e08d..a730183 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,12 +15,11 @@
  */
 package com.android.quickstep.inputconsumers;
 
-import static com.android.launcher3.Utilities.createHomeIntent;
 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_HOME_GESTURE;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.graphics.PointF;
 import android.view.MotionEvent;
@@ -79,11 +78,7 @@
 
     @Override
     public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
-        try {
-            mContext.startActivity(mGestureState.getHomeIntent());
-        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
-            mContext.startActivity(createHomeIntent());
-        }
+        startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
         ActiveGestureLog.INSTANCE.addLog("startQuickstep");
         BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
         int state = (mGestureState != null && mGestureState.getEndTarget() != null)
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index caf61c7..66ed056 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.Utilities.mapRange;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 
 import android.animation.Animator;
 import android.app.Activity;
@@ -148,45 +149,50 @@
     }
 
     private void startBackgroundAnimation() {
-        if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported(
-                VibrationEffect.Composition.PRIMITIVE_THUD)) {
-            if (mBackgroundAnimatorListener == null) {
-                mBackgroundAnimatorListener =
-                        new Animator.AnimatorListener() {
-                            @Override
-                            public void onAnimationStart(Animator animation) {
-                                runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
-                            }
-
-                            @Override
-                            public void onAnimationRepeat(Animator animation) {
-                                runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
-                            }
-
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                runOnUiHelperThread(mVibrator::cancel);
-                            }
-
-                            @Override
-                            public void onAnimationCancel(Animator animation) {
-                                runOnUiHelperThread(mVibrator::cancel);
-                            }
-                        };
-            }
-            mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+        if (!Utilities.ATLEAST_S || mVibrator == null) {
+            return;
         }
-        mAnimatedBackground.playAnimation();
-    }
+        boolean supportsThud = mVibrator.areAllPrimitivesSupported(
+                VibrationEffect.Composition.PRIMITIVE_THUD);
 
-    /**
-     * Sets up the vibration effect for the next round of animation. The parameters vary between
-     * different illustrations.
-     */
-    private VibrationEffect getVibrationEffect() {
-        return VibrationEffect.startComposition()
-                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, 50)
-                .compose();
+        if (!supportsThud && !mVibrator.areAllPrimitivesSupported(
+                VibrationEffect.Composition.PRIMITIVE_TICK)) {
+            return;
+        }
+        if (mBackgroundAnimatorListener == null) {
+            VibrationEffect vibrationEffect = VibrationEffect.startComposition()
+                    .addPrimitive(supportsThud
+                                    ? VibrationEffect.Composition.PRIMITIVE_THUD
+                                    : VibrationEffect.Composition.PRIMITIVE_TICK,
+                            /* scale= */ 1.0f,
+                            /* delay= */ 50)
+                    .compose();
+
+            mBackgroundAnimatorListener =
+                    new Animator.AnimatorListener() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect));
+                        }
+
+                        @Override
+                        public void onAnimationRepeat(Animator animation) {
+                            runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect));
+                        }
+
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            runOnUiHelperThread(mVibrator::cancel);
+                        }
+
+                        @Override
+                        public void onAnimationCancel(Animator animation) {
+                            runOnUiHelperThread(mVibrator::cancel);
+                        }
+                    };
+        }
+        mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+        mAnimatedBackground.playAnimation();
     }
 
     @Override
@@ -282,7 +288,7 @@
         @Override
         public boolean performAccessibilityAction(View host, int action, Bundle args) {
             if (action == AccessibilityAction.ACTION_CLICK.getId()) {
-                startActivity(Utilities.createHomeIntent());
+                startHomeIntentSafely(AllSetActivity.this, null);
                 finish();
                 return true;
             }
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index c980d1e..45fc3af 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,5 +1,6 @@
 package com.android.quickstep.views;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -19,6 +20,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.LauncherAnimUtils;
@@ -110,13 +112,19 @@
      */
     public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
             View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) {
-        final BaseDragLayer dragLayer = launcher.getDragLayer();
-        ViewGroup parent = (ViewGroup) dragLayer.getParent();
+        final ViewGroup dragLayer = launcher.getDragLayer();
         final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater()
-                .inflate(R.layout.floating_split_select_view, parent, false);
+                .inflate(R.layout.floating_split_select_view, dragLayer, false);
 
         floatingView.init(launcher, originalView, thumbnail, icon, positionOut);
-        parent.addView(floatingView);
+        // Add this animating view underneath the existing open task menu view (if there is one)
+        View openTaskView = AbstractFloatingView.getOpenView(launcher, TYPE_TASK_MENU);
+        int openTaskViewIndex = dragLayer.indexOfChild(openTaskView);
+        if (openTaskViewIndex == -1) {
+            // Add to top if not
+            openTaskViewIndex = dragLayer.getChildCount();
+        }
+        dragLayer.addView(floatingView, openTaskViewIndex - 1);
         return floatingView;
     }
 
@@ -125,7 +133,7 @@
         Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), originalView, viewBounds,
                 false /* ignoreTransform */, null /* recycle */,
                 mStartingPosition);
-        final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
+        final BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
                 Math.round(mStartingPosition.width()),
                 Math.round(mStartingPosition.height()));
         initPosition(mStartingPosition, lp);
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1c4e497..3133453 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -22,10 +22,8 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -233,10 +231,6 @@
             return 0;
         }
 
-        if (mDp.isVerticalBarLayout()) {
-            return mDp.getInsets().bottom;
-        }
-
         if (!mDp.isGestureMode && mDp.isTaskbarPresent) {
             return mDp.getOverviewActionsClaimedSpaceBelow();
         }
@@ -254,12 +248,6 @@
         mTaskSize.set(taskSize);
         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
 
-        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
-                dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing,
-                ViewGroup.LayoutParams.MATCH_PARENT);
-        params.weight = dp.isVerticalBarLayout() ? 1 : 0;
-        findViewById(R.id.action_split_space).setLayoutParams(params);
-
         requestLayout();
 
         mSplitButton.setCompoundDrawablesWithIntrinsicBounds(
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2360396..364bd59 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -34,6 +34,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -630,6 +631,8 @@
     private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
             R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
 
+    private SplitInstructionsView mSplitInstructionsView;
+
     @Nullable
     private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource;
 
@@ -2764,11 +2767,15 @@
             mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
                     false /* fadeWithThumbnail */, true /* isStagedTask */);
         }
+
+        mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity);
+        mSplitInstructionsView.setAlpha(0);
+        anim.addFloat(mSplitInstructionsView, SplitInstructionsView.ALPHA_FLOAT, 0, 1, ACCEL);
+
         InteractionJankMonitorWrapper.begin(this,
                 InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
         anim.addEndListener(success -> {
             if (success) {
-                mSplitToast.show();
                 InteractionJankMonitorWrapper.end(
                         InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
             } else {
@@ -4091,6 +4098,10 @@
         mSecondSplitHiddenView.setVisibility(INVISIBLE);
         InteractionJankMonitorWrapper.begin(this,
                 InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
+
+        // Fade out all other views underneath placeholders
+        ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0);
+        pendingAnimation.add(tvFade, DEACCEL_2, SpringProperty.DEFAULT);
         pendingAnimation.buildAnim().start();
         return true;
     }
@@ -4099,12 +4110,16 @@
     @SuppressLint("WrongCall")
     protected void resetFromSplitSelectionState() {
         if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
+            if (mSplitInstructionsView != null) {
+                mActivity.getDragLayer().removeView(mSplitInstructionsView);
+                mSplitInstructionsView = null;
+            }
             if (mFirstFloatingTaskView != null) {
-                mActivity.getRootView().removeView(mFirstFloatingTaskView);
+                mActivity.getDragLayer().removeView(mFirstFloatingTaskView);
                 mFirstFloatingTaskView = null;
             }
             if (mSecondFloatingTaskView != null) {
-                mActivity.getRootView().removeView(mSecondFloatingTaskView);
+                mActivity.getDragLayer().removeView(mSecondFloatingTaskView);
                 mSecondFloatingTaskView = null;
                 mSecondSplitHiddenView.setVisibility(VISIBLE);
                 mSecondSplitHiddenView = null;
@@ -4164,6 +4179,10 @@
         taskViewsFloat.first.set(this, getSplitSelectTranslation());
         taskViewsFloat.second.set(this, 0f);
 
+        if (mSplitInstructionsView != null) {
+            mSplitInstructionsView.ensureProperRotation();
+        }
+
         applySplitPrimaryScrollOffset();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
new file mode 100644
index 0000000..7d94505
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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.views;
+
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.DisplayController;
+
+/**
+ * A rounded rectangular component containing a single TextView.
+ * Appears when a split is in progress, and tells the user to select a second app to initiate
+ * splitscreen.
+ *
+ * Appears and disappears concurrently with a FloatingTaskView.
+ */
+public class SplitInstructionsView extends FrameLayout {
+    private final StatefulActivity mLauncher;
+
+    public static final FloatProperty<SplitInstructionsView> ALPHA_FLOAT =
+            new FloatProperty<SplitInstructionsView>("SplitInstructionsAlpha") {
+                @Override
+                public void setValue(SplitInstructionsView splitInstructionsView, float v) {
+                    splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
+                    splitInstructionsView.setAlpha(v);
+                }
+
+                @Override
+                public Float get(SplitInstructionsView splitInstructionsView) {
+                    return splitInstructionsView.getAlpha();
+                }
+            };
+
+    public SplitInstructionsView(Context context) {
+        this(context, null);
+    }
+
+    public SplitInstructionsView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SplitInstructionsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = (StatefulActivity) context;
+    }
+
+    static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
+        ViewGroup dragLayer = launcher.getDragLayer();
+        final SplitInstructionsView splitInstructionsView =
+                (SplitInstructionsView) launcher.getLayoutInflater().inflate(
+                        R.layout.split_instructions_view,
+                        dragLayer,
+                        false
+                );
+
+        dragLayer.addView(splitInstructionsView);
+        return splitInstructionsView;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        ensureProperRotation();
+    }
+
+    void ensureProperRotation() {
+        ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler()
+                .setSplitInstructionsParams(
+                        this,
+                        mLauncher.getDeviceProfile(),
+                        getMeasuredHeight(),
+                        getMeasuredWidth(),
+                        getThreeButtonNavShift()
+                );
+    }
+
+    // In some cases, when user is using 3-button nav, there isn't enough room for both the
+    // 3-button nav and a centered SplitInstructionsView. This function will return an int that will
+    // be used to shift the SplitInstructionsView over a bit so that everything looks well-spaced.
+    // In many cases, this will return 0, since we don't need to shift it away from the center.
+    int getThreeButtonNavShift() {
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS)
+                && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))) {
+            int navButtonWidth = getResources().getDimensionPixelSize(
+                    R.dimen.taskbar_nav_buttons_size);
+            int extraMargin = getResources().getDimensionPixelSize(
+                    R.dimen.taskbar_contextual_button_margin);
+            // Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
+            // up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
+            // center of the screen and we want to center it in the remaining space, therefore we
+            // want to shift it over by half the 3-button layout's width.
+            // If the user is using an RtL layout, we shift it the opposite way.
+            return -((3 * navButtonWidth + extraMargin) / 2) * (isLayoutRtl() ? -1 : 1);
+        } else {
+            return 0;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 3803f1b..bdf21b8 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,7 +16,6 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
@@ -245,17 +244,9 @@
         LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
         mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
                 menuOptionView, mActivity.getDeviceProfile());
-        menuOptionView.setOnClickListener(view -> {
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-                RecentsView recentsView = mTaskView.getRecentsView();
-                recentsView.switchToScreenshot(null,
-                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
-                                false /* shouldPip */,
-                                () -> menuOption.onClick(view)));
-            } else {
-                menuOption.onClick(view);
-            }
-        });
+        // Set an onClick listener on each menu option. The onClick method is responsible for
+        // ending LiveTile mode on the thumbnail if needed.
+        menuOptionView.setOnClickListener(menuOption::onClick);
         mOptionLayout.addView(menuOptionView);
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d58bb7c..f0ca46a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1537,7 +1537,6 @@
     }
 
     public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
-        AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
         getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
     }
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8403af4..39f0a2b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -391,8 +391,17 @@
     <dimen name="split_placeholder_inset">16dp</dimen>
     <dimen name="split_placeholder_icon_size">44dp</dimen>
     <dimen name="task_menu_width_grid">216dp</dimen>
-
-
+    <dimen name="split_instructions_radius">22dp</dimen>
+    <dimen name="split_instructions_elevation">1dp</dimen>
+    <dimen name="split_instructions_horizontal_padding">24dp</dimen>
+    <dimen name="split_instructions_vertical_padding">12dp</dimen>
+    <dimen name="split_instructions_bottom_margin_tablet_landscape">32dp</dimen>
+    <dimen name="split_instructions_bottom_margin_tablet_portrait">44dp</dimen>
+    <dimen name="split_instructions_bottom_margin_twopanels_landscape">33dp</dimen>
+    <dimen name="split_instructions_bottom_margin_twopanels_portrait">51dp</dimen>
+    <dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
+    <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
+    
     <!-- Workspace grid visualization parameters -->
     <dimen name="grid_visualization_rounding_radius">28dp</dimen>
     <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index b276397..a428b7e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -414,10 +414,7 @@
         overviewTaskIconDrawableSizeGridPx =
                 res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
         overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
-        // In vertical bar, use the smaller task margin for the top regardless of mode.
-        overviewActionsTopMarginPx = isVerticalBarLayout()
-                ? overviewTaskMarginPx
-                : res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
+        overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
         overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
         overviewActionsButtonSpacing = res.getDimensionPixelSize(
                 R.dimen.overview_actions_button_spacing);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7b96838..dc8c739 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -587,15 +587,6 @@
     }
 
     /**
-     * Returns an intent for starting the default home activity
-     */
-    public static Intent createHomeIntent() {
-        return new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-    }
-
-    /**
      * Wraps a message with a TTS span, so that a different message is spoken than
      * what is getting displayed.
      * @param msg original message
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 2c04fc7..74b9014 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -250,7 +250,7 @@
     public boolean shouldContainerScroll(MotionEvent ev) {
         BaseDragLayer dragLayer = mActivityContext.getDragLayer();
         // Scroll if not within the container view (e.g. over large-screen scrim).
-        if (!dragLayer.isEventOverView(this, ev)) {
+        if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
             return true;
         }
         if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index ef9250c..a7b0b9d 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -56,6 +56,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -267,38 +268,33 @@
 
     /** Return what's in the src but not in the dest */
     private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
-        Set<String> destIntentSet = new HashSet<>();
-        Set<Map<String, Integer>> destFolderIntentSet = new HashSet<>();
+        Map<String, Integer> destIdSet = new HashMap<>();
         for (DbEntry entry : dest) {
-            if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-                destFolderIntentSet.add(getFolderIntents(entry));
+            String entryID = entry.getEntryMigrationId();
+            if (destIdSet.containsKey(entryID)) {
+                destIdSet.put(entryID, destIdSet.get(entryID) + 1);
             } else {
-                destIntentSet.add(entry.mIntent);
+                destIdSet.put(entryID, 1);
             }
         }
         List<DbEntry> diff = new ArrayList<>();
         for (DbEntry entry : src) {
-            if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-                if (!destFolderIntentSet.contains(getFolderIntents(entry))) {
+            String entryID = entry.getEntryMigrationId();
+            if (destIdSet.containsKey(entryID)) {
+                Integer count = destIdSet.get(entryID);
+                if (count <= 0) {
                     diff.add(entry);
+                    destIdSet.remove(entryID);
+                } else {
+                    destIdSet.put(entryID, count - 1);
                 }
             } else {
-                if (!destIntentSet.contains(entry.mIntent)) {
-                    diff.add(entry);
-                }
+                diff.add(entry);
             }
         }
         return diff;
     }
 
-    private static Map<String, Integer> getFolderIntents(DbEntry entry) {
-        Map<String, Integer> folder = new HashMap<>();
-        for (String intent : entry.mFolderItems.keySet()) {
-            folder.put(intent, entry.mFolderItems.get(intent).size());
-        }
-        return folder;
-    }
-
     private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
             String srcTableName, String destTableName) {
         int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
@@ -780,5 +776,31 @@
             values.put(LauncherSettings.Favorites.SPANX, spanX);
             values.put(LauncherSettings.Favorites.SPANY, spanY);
         }
+
+        /**
+         * This method should return an id that should be the same for two folders containing the
+         * same elements.
+         */
+        private String getFolderMigrationId() {
+            return mFolderItems.keySet().stream()
+                    .map(intentString -> mFolderItems.get(intentString).size() + intentString)
+                    .sorted()
+                    .collect(Collectors.joining(","));
+        }
+
+        /** This id is not used in the DB is only used while doing the migration and it identifies
+         * an entry on each workspace. For example two calculator icons would have the same
+         * migration id even thought they have different database ids.
+         */
+        public String getEntryMigrationId() {
+            switch (itemType) {
+                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+                    return getFolderMigrationId();
+                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                    return mProvider;
+                default:
+                    return mIntent;
+            }
+        }
     }
 }
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index b477905..c43a4d7 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -19,6 +19,7 @@
 import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.Gravity.END;
+import static android.view.Gravity.LEFT;
 import static android.view.Gravity.START;
 import static android.view.Gravity.TOP;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -436,6 +437,28 @@
     }
 
     @Override
+    public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+            int splitInstructionsWidth, int threeButtonNavShift) {
+        out.setPivotX(0);
+        out.setPivotY(0);
+        out.setRotation(getDegreesRotated());
+        int distanceToEdge = out.getResources().getDimensionPixelSize(
+                R.dimen.split_instructions_bottom_margin_phone_landscape);
+        // Adjust for any insets on the left edge
+        int insetCorrectionX = dp.getInsets().left;
+        // Center the view in case of unbalanced insets on top or bottom of screen
+        int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
+        out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX);
+        out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f)
+                + insetCorrectionY);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
+        // Setting gravity to LEFT instead of the lint-recommended START because we always want this
+        // view to be screen-left when phone is in landscape, regardless of the RtL setting.
+        lp.gravity = LEFT | CENTER_VERTICAL;
+        out.setLayoutParams(lp);
+    }
+
+    @Override
     public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
             @StagePosition int stagePosition, Rect out1, Rect out2) {
         // In fake land/seascape, the window bounds are always top and bottom half
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index ca46aa8..2d9d95c 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -135,6 +135,16 @@
             @StagePosition int stagePosition);
 
     /**
+     * Sets positioning and rotation for a SplitInstructionsView.
+     * @param out The SplitInstructionsView that needs to be positioned.
+     * @param dp The device profile, used to report rotation and device type.
+     * @param splitInstructionsHeight The SplitInstructionView's height.
+     * @param splitInstructionsWidth  The SplitInstructionView's width.
+     */
+    void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+            int splitInstructionsWidth, int threeButtonNavShift);
+
+    /**
      * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
      * @param stagePosition the split position option (top/left, bottom/right) of the first
      *                           task selected for entering split
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 508823c..942c191 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 
@@ -47,7 +48,9 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
@@ -494,6 +497,58 @@
     }
 
     @Override
+    public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+            int splitInstructionsWidth, int threeButtonNavShift) {
+        out.setPivotX(0);
+        out.setPivotY(0);
+        out.setRotation(getDegreesRotated());
+        int distanceToEdge;
+        if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
+                && (dp.isTwoPanels || dp.isTablet)) {
+            // If 3-button nav is active, align the splitInstructionsView with it.
+            distanceToEdge = dp.getTaskbarOffsetY()
+                    + ((dp.taskbarSize - splitInstructionsHeight) / 2);
+        } else {
+            // If 3-button nav is not active, set bottom margin according to spec.
+            if (dp.isPhone) {
+                if (dp.isLandscape) {
+                    distanceToEdge = out.getResources().getDimensionPixelSize(
+                            R.dimen.split_instructions_bottom_margin_phone_landscape);
+                } else {
+                    distanceToEdge = out.getResources().getDimensionPixelSize(
+                            R.dimen.split_instructions_bottom_margin_phone_portrait);
+                }
+            } else if (dp.isTwoPanels) {
+                if (dp.isLandscape) {
+                    distanceToEdge = out.getResources().getDimensionPixelSize(
+                            R.dimen.split_instructions_bottom_margin_twopanels_landscape);
+                } else {
+                    distanceToEdge = out.getResources().getDimensionPixelSize(
+                            R.dimen.split_instructions_bottom_margin_twopanels_portrait);
+                }
+            } else {
+                if (dp.isLandscape) {
+                    distanceToEdge = out.getResources().getDimensionPixelSize(
+                            R.dimen.split_instructions_bottom_margin_tablet_landscape);
+                } else {
+                    distanceToEdge = out.getResources().getDimensionPixelSize(
+                            R.dimen.split_instructions_bottom_margin_tablet_portrait);
+                }
+            }
+        }
+
+        // Center the view in case of unbalanced insets on left or right of screen
+        int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
+        // Adjust for any insets on the bottom edge
+        int insetCorrectionY = dp.getInsets().bottom;
+        out.setTranslationX(insetCorrectionX + threeButtonNavShift);
+        out.setTranslationY(-distanceToEdge + insetCorrectionY);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
+        lp.gravity = CENTER_HORIZONTAL | BOTTOM;
+        out.setLayoutParams(lp);
+    }
+
+    @Override
     public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
             @StagePosition int stagePosition, Rect out1, Rect out2) {
         int screenHeight = dp.heightPx;
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 74b6a5b..71adb7a 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -19,6 +19,7 @@
 import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.Gravity.END;
+import static android.view.Gravity.RIGHT;
 import static android.view.Gravity.START;
 
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@@ -167,6 +168,29 @@
     }
 
     @Override
+    public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+            int splitInstructionsWidth, int threeButtonNavShift) {
+        out.setPivotX(0);
+        out.setPivotY(0);
+        out.setRotation(getDegreesRotated());
+        int distanceToEdge = out.getResources().getDimensionPixelSize(
+                R.dimen.split_instructions_bottom_margin_phone_landscape);
+        // Adjust for any insets on the right edge
+        int insetCorrectionX = dp.getInsets().right;
+        // Center the view in case of unbalanced insets on top or bottom of screen
+        int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
+        out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge
+                + insetCorrectionX);
+        out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f)
+                + insetCorrectionY);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
+        // Setting gravity to RIGHT instead of the lint-recommended END because we always want this
+        // view to be screen-right when phone is in seascape, regardless of the RtL setting.
+        lp.gravity = RIGHT | CENTER_VERTICAL;
+        out.setLayoutParams(lp);
+    }
+
+    @Override
     public void setTaskIconParams(FrameLayout.LayoutParams iconParams,
             int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 7c73be5..9ed6bee 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -55,6 +55,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Objects;
 import java.util.Set;
@@ -238,7 +239,8 @@
         if (newInfo.navigationMode != oldInfo.navigationMode) {
             change |= CHANGE_NAVIGATION_MODE;
         }
-        if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
+        if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)
+                || !newInfo.mPerDisplayBounds.equals(oldInfo.mPerDisplayBounds)) {
             change |= CHANGE_SUPPORTED_BOUNDS;
 
             Point currentS = newInfo.currentSize;
@@ -261,6 +263,9 @@
                 return;
             }
         }
+        Log.d("b/198965093", "handleInfoChange"
+                + "\n\tchange: " + change
+                + "\n\tConfiguration diff: " + newInfo.mConfiguration.diff(oldInfo.mConfiguration));
 
         if (change != 0) {
             mInfo = newInfo;
@@ -300,6 +305,9 @@
         private final ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> mPerDisplayBounds =
                 new ArrayMap<>();
 
+        // TODO(b/198965093): Remove after investigation
+        private Configuration mConfiguration;
+
         public Info(Context context, Display display) {
             /* don't need system overrides for external displays */
             this(context, display, new WindowManagerProxy(), new ArrayMap<>());
@@ -321,6 +329,9 @@
             mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
             navigationMode = parseNavigationMode(context);
 
+            // TODO(b/198965093): Remove after investigation
+            mConfiguration = config;
+
             mPerDisplayBounds.putAll(perDisplayBoundsCache);
             Pair<CachedDisplayInfo, WindowBounds[]> cachedValue = mPerDisplayBounds.get(displayId);
 
@@ -340,8 +351,11 @@
             }
             mPerDisplayBounds.values().forEach(
                     pair -> Collections.addAll(supportedBounds, pair.second));
-            Log.d("b/211775278", "displayId: " + displayId + ", currentSize: " + currentSize);
-            Log.d("b/211775278", "perDisplayBounds: " + mPerDisplayBounds);
+            Log.e("b/198965093", "mConfiguration: " + mConfiguration);
+            Log.d("b/198965093", "displayInfo: " + displayInfo);
+            Log.d("b/198965093", "realBounds: " + realBounds);
+            mPerDisplayBounds.values().forEach(pair -> Log.d("b/198965093",
+                    "perDisplayBounds - " + pair.first + ": " + Arrays.deepToString(pair.second)));
         }
 
         /**
@@ -375,7 +389,8 @@
         pw.println("  densityDpi=" + info.densityDpi);
         pw.println("  navigationMode=" + info.navigationMode.name());
         pw.println("  currentSize=" + info.currentSize);
-        pw.println("  supportedBounds=" + info.supportedBounds);
+        info.mPerDisplayBounds.values().forEach(pair -> pw.println(
+                "  perDisplayBounds - " + pair.first + ": " + Arrays.deepToString(pair.second)));
     }
 
     /**
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index a15679a..91480e1 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -76,6 +76,7 @@
                 + "bounds=" + bounds
                 + ", insets=" + insets
                 + ", availableSize=" + availableSize
+                + ", rotationHint=" + rotationHint
                 + '}';
     }
 
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 8a97c6b..e00b569 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -53,7 +53,6 @@
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -199,8 +198,8 @@
         // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
         executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
                 isWorkspaceScrollable(launcher)));
-        assertNull("Chrome app was found on empty workspace",
-                workspace.tryGetWorkspaceAppIcon("Chrome"));
+        workspace.verifyWorkspaceAppIconIsGone(
+                "Chrome app was found on empty workspace", "Chrome");
 
         workspace.ensureWorkspaceIsScrollable();
 
@@ -376,6 +375,7 @@
 
     @Test
     @PortraitLandscape
+    @ScreenRecord
     public void testDragToFolder() {
         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
         // on tablets or phones due to difference in resolution.
@@ -388,10 +388,10 @@
         folder.getAppIcon(GMAIL_APP_NAME);
         Workspace workspace = folder.close();
 
-        assertNull(STORE_APP_NAME + " should be moved to a folder.",
-                workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME));
-        assertNull(GMAIL_APP_NAME + " should be moved to a folder.",
-                workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME));
+        workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.",
+                STORE_APP_NAME);
+        workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.",
+                GMAIL_APP_NAME);
 
         final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
         folderIcon = mapIcon.dragToIcon(folderIcon);
@@ -399,8 +399,8 @@
         folder.getAppIcon(MAPS_APP_NAME);
         workspace = folder.close();
 
-        assertNull(MAPS_APP_NAME + " should be moved to a folder.",
-                workspace.tryGetWorkspaceAppIcon(MAPS_APP_NAME));
+        workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.",
+                MAPS_APP_NAME);
     }
 
     @Test
@@ -424,8 +424,9 @@
         for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
             final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
             Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon);
-            assertNull(appName + " app was found after being deleted from workspace",
-                    workspace.tryGetWorkspaceAppIcon(appName));
+            workspace.verifyWorkspaceAppIconIsGone(
+                    appName + " app was found after being deleted from workspace",
+                    appName);
         }
     }
 
@@ -509,10 +510,8 @@
                     .containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME);
 
             mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
-
-            assertNull(
-                    DUMMY_APP_NAME + " app was found after being uninstalled",
-                    mLauncher.getWorkspace().tryGetWorkspaceAppIcon(DUMMY_APP_NAME));
+            mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
+                    DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
 
             Map<String, Point> finalPositions =
                     mLauncher.getWorkspace().getWorkspaceIconsPositions();
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index eb7f05b..e919740 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -140,6 +140,20 @@
         }
     }
 
+    /**
+     * Waits for an app icon to be gone (e.g. after uninstall). Fails if it remains.
+     *
+     * @param errorMessage error message thrown then the icon doesn't disappear.
+     * @param appName      app that should be gone.
+     */
+    public void verifyWorkspaceAppIconIsGone(String errorMessage, String appName) {
+        final UiObject2 workspace = verifyActiveContainer();
+        assertTrue(errorMessage,
+                workspace.wait(
+                        Until.gone(AppIcon.getAppIconSelector(appName, mLauncher)),
+                        LauncherInstrumentation.WAIT_TIME_MS));
+    }
+
 
     /**
      * Returns an icon for the app; fails if the icon doesn't exist.