Merge "Revert "Combining the Thumbnail bitmap with the background to fix background spilling in the front."" into tm-dev
diff --git a/Android.bp b/Android.bp
index f5a38b4..5a153a7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -208,6 +208,8 @@
     srcs: [
         "ext_tests/src/**/*.java",
         "ext_tests/src/**/*.kt",
+        "quickstep/ext_tests/src/**/*.java",
+        "quickstep/ext_tests/src/**/*.kt",
     ],
 }
 
@@ -224,6 +226,21 @@
     ],
 }
 
+// Common source files used to build go launcher
+filegroup {
+    name: "launcher-go-src-no-build-config",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+        "quickstep/src/**/*.java",
+        "quickstep/src/**/*.kt",
+        "go/src/**/*.java",
+        "go/src/**/*.kt",
+        "go/quickstep/src/**/*.java",
+        "go/quickstep/src/**/*.kt",
+    ],
+}
+
 // Proguard files for Launcher3
 filegroup {
     name: "launcher-proguard-rules",
diff --git a/OWNERS b/OWNERS
index 05fa502..7f98ea6 100644
--- a/OWNERS
+++ b/OWNERS
@@ -39,6 +39,7 @@
 xuqiu@google.com
 sreyasr@google.com
 thiruram@google.com
+brianji@google.com
 
 per-file FeatureFlags.java, globs = set noparent
 per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com
diff --git a/build.gradle b/build.gradle
index e4ade88..68ed73d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -170,7 +170,7 @@
 protobuf {
     // Configure the protoc executable
     protoc {
-        artifact = "com.google.protobuf:protoc:${protocVersion}"
+        artifact = "com.google.protobuf:protoc:${protocVersion}${PROTO_ARCH_SUFFIX}"
     }
     generateProtoTasks {
         all().each { task ->
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 02206c0..d16e12c 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -29,8 +29,10 @@
 import androidx.annotation.Keep;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutAndWidgetContainer;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -205,6 +207,32 @@
                 }
             }
 
+            case TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT: {
+                useTestWorkspaceLayout(true);
+                return response;
+            }
+
+            case TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT: {
+                useTestWorkspaceLayout(false);
+                return response;
+            }
+
+            case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
+                return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
+                    ShortcutAndWidgetContainer hotseatIconsContainer =
+                            l.getHotseat().getShortcutsAndWidgets();
+                    ArrayList<String> hotseatIconNames = new ArrayList<>();
+
+                    for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
+                        // Use unchecked cast to catch changes in hotseat layout
+                        BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
+                        hotseatIconNames.add((String) icon.getText());
+                    }
+
+                    return hotseatIconNames;
+                });
+            }
+
             case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
                 return response;
@@ -223,4 +251,15 @@
                 return super.call(method, arg, extras);
         }
     }
+
+    private void useTestWorkspaceLayout(boolean useTestWorkspaceLayout) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            LauncherSettings.Settings.call(mContext.getContentResolver(), useTestWorkspaceLayout
+                    ? LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG
+                    : LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
 }
diff --git a/go/quickstep/res/values-bn/strings.xml b/go/quickstep/res/values-bn/strings.xml
index 6067191..8c27e63 100644
--- a/go/quickstep/res/values-bn/strings.xml
+++ b/go/quickstep/res/values-bn/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"অ্যাপ শেয়ার করুন"</string>
     <string name="action_listen" msgid="2370304050784689486">"শুনুন"</string>
     <string name="action_translate" msgid="8028378961867277746">"অনুবাদ করুন"</string>
-    <string name="action_search" msgid="6269564710943755464">"লেন্স"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"বুঝেছি"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"বাতিল করুন"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"সেটিংস"</string>
diff --git a/go/quickstep/res/values-gu/strings.xml b/go/quickstep/res/values-gu/strings.xml
index faa66fc..80d9e2e 100644
--- a/go/quickstep/res/values-gu/strings.xml
+++ b/go/quickstep/res/values-gu/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"ઍપ શેર કરો"</string>
     <string name="action_listen" msgid="2370304050784689486">"સાંભળો"</string>
     <string name="action_translate" msgid="8028378961867277746">"અનુવાદ કરો"</string>
-    <string name="action_search" msgid="6269564710943755464">"લેન્સ"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"સમજાઈ ગયું"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"રદ કરો"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"સેટિંગ"</string>
diff --git a/go/quickstep/res/values-kk/strings.xml b/go/quickstep/res/values-kk/strings.xml
index 2361cbb..2a2f96d 100644
--- a/go/quickstep/res/values-kk/strings.xml
+++ b/go/quickstep/res/values-kk/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"Қолданбаны бөлісу"</string>
     <string name="action_listen" msgid="2370304050784689486">"Тыңдау"</string>
     <string name="action_translate" msgid="8028378961867277746">"Аудару"</string>
-    <string name="action_search" msgid="6269564710943755464">"Объектив"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"ТҮСІНІКТІ"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"БАС ТАРТУ"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"ПАРАМЕТРЛЕР"</string>
diff --git a/go/quickstep/res/values-ml/strings.xml b/go/quickstep/res/values-ml/strings.xml
index 19d0b75..ed57395 100644
--- a/go/quickstep/res/values-ml/strings.xml
+++ b/go/quickstep/res/values-ml/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"ആപ്പ് പങ്കിടുക"</string>
     <string name="action_listen" msgid="2370304050784689486">"കേൾക്കുക"</string>
     <string name="action_translate" msgid="8028378961867277746">"വിവർത്തനം ചെയ്യുക"</string>
-    <string name="action_search" msgid="6269564710943755464">"ലെൻസ്"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"മനസ്സിലായി"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"റദ്ദാക്കുക"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"ക്രമീകരണം"</string>
diff --git a/go/quickstep/res/values-mn/strings.xml b/go/quickstep/res/values-mn/strings.xml
index 2eb3c98..d03b2d2 100644
--- a/go/quickstep/res/values-mn/strings.xml
+++ b/go/quickstep/res/values-mn/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"Аппыг хуваалцах"</string>
     <string name="action_listen" msgid="2370304050784689486">"Сонсох"</string>
     <string name="action_translate" msgid="8028378961867277746">"Орчуулах"</string>
-    <string name="action_search" msgid="6269564710943755464">"Дуран"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"ОЙЛГОЛОО"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"ЦУЦЛАХ"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"ТОХИРГОО"</string>
diff --git a/go/quickstep/res/values-ms/strings.xml b/go/quickstep/res/values-ms/strings.xml
index 36e1678..3b0a7fb 100644
--- a/go/quickstep/res/values-ms/strings.xml
+++ b/go/quickstep/res/values-ms/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"Kongsi Apl"</string>
     <string name="action_listen" msgid="2370304050784689486">"Dengar"</string>
     <string name="action_translate" msgid="8028378961867277746">"Terjemah"</string>
-    <string name="action_search" msgid="6269564710943755464">"Kanta"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"OK"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"BATAL"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"TETAPAN"</string>
diff --git a/go/quickstep/res/values-or/strings.xml b/go/quickstep/res/values-or/strings.xml
index 36204a3..2e76e2d 100644
--- a/go/quickstep/res/values-or/strings.xml
+++ b/go/quickstep/res/values-or/strings.xml
@@ -7,13 +7,13 @@
     <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"ବୁଝିଗଲି"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"ବାତିଲ୍ କରନ୍ତୁ"</string>
-    <string name="dialog_settings" msgid="6564397136021186148">"ସେଟିଂସ୍"</string>
+    <string name="dialog_settings" msgid="6564397136021186148">"ସେଟିଂସ"</string>
     <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ସ୍କିନରେ ଥିବା ଟେକ୍ସଟକୁ ଅନୁବାଦ କରନ୍ତୁ କିମ୍ବା ଶୁଣନ୍ତୁ"</string>
-    <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଟେକ୍ସଟ୍, ୱେବ୍ ଠିକଣା ଏବଂ ସ୍କ୍ରିନସଟଗୁଡ଼ିକ ପରି ସୂଚନାକୁ Google ସହ ସେୟାର୍ କରାଯାଇପାରେ।\n\nଆପଣ କେଉଁ ସୂଚନା ସେୟାର୍ କରନ୍ତି ତାହା ପରିବର୍ତ୍ତନ କରିବାକୁ, "<b>"ସେଟିଂସ୍ &gt; ଆପ୍ସ &gt; ଡିଫଲ୍ଟ ଆପ୍ସ &gt; ଡିଜିଟାଲ୍ ଆସିଷ୍ଟାଣ୍ଟ ଆପ"</b>"କୁ ଯାଆନ୍ତୁ।"</string>
+    <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଟେକ୍ସଟ, ୱେବ ଠିକଣା ଏବଂ ସ୍କ୍ରିନସଟଗୁଡ଼ିକ ପରି ସୂଚନାକୁ Google ସହ ସେୟାର କରାଯାଇପାରେ।\n\nଆପଣ କେଉଁ ସୂଚନାକୁ ସେୟାର କରନ୍ତି ତାହା ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, "<b>"ସେଟିଂସ &gt; ଆପ୍ସ &gt; ଡିଫଲ୍ଟ ଆପ୍ସ &gt; Digital assistant ଆପ"</b>"କୁ ଯାଆନ୍ତୁ।"</string>
     <string name="assistant_not_selected_title" msgid="5017072974603345228">"ଏହି ଫିଚର୍ ବ୍ୟବହାର କରିବାକୁ ଏକ ଆସିଷ୍ଟାଣ୍ଟ ବାଛନ୍ତୁ"</string>
-    <string name="assistant_not_selected_text" msgid="3244613673884359276">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା ପାଇଁ କିମ୍ବା ଅନୁବାଦ କରିବାକୁ, ସେଟିଂସରେ ଏକ ଡିଜିଟାଲ୍ ଆସିଷ୍ଟାଣ୍ଟ ଆପ୍ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="assistant_not_selected_text" msgid="3244613673884359276">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା କିମ୍ବା ଅନୁବାଦ କରିବା ପାଇଁ, ସେଟିଂସରେ ଏକ digital assistant ଆପ ବାଛନ୍ତୁ"</string>
     <string name="assistant_not_supported_title" msgid="1675788067597484142">"ଏହି ଫିଚର୍ ବ୍ୟବହାର କରିବା ପାଇଁ ଆପଣଙ୍କ ଆସିଷ୍ଟାଣ୍ଟକୁ ବଦଳାନ୍ତୁ"</string>
-    <string name="assistant_not_supported_text" msgid="1708031078549268884">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା ପାଇଁ କିମ୍ବା ଅନୁବାଦ କରିବାକୁ, ସେଟିଂସରେ ଆପଣଙ୍କ ଡିଜିଟାଲ୍ ଆସିଷ୍ଟାଣ୍ଟ ଆପକୁ ବଦଳାନ୍ତୁ"</string>
+    <string name="assistant_not_supported_text" msgid="1708031078549268884">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା କିମ୍ବା ଅନୁବାଦ କରିବା ପାଇଁ, ସେଟିଂସରେ ଆପଣଙ୍କ Digital assistant ଆପକୁ ବଦଳାନ୍ତୁ"</string>
     <string name="tooltip_listen" msgid="7634466447860989102">"ଏହି ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା ପାଇଁ ଏଠାରେ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="tooltip_translate" msgid="4184845868901542567">"ଏହି ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଅନୁବାଦ କରିବା ପାଇଁ ଏଠାରେ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"ଏହି ଆପ ସେୟାର କରାଯାଇପାରିବ ନାହିଁ"</string>
diff --git a/go/quickstep/res/values-ta/strings.xml b/go/quickstep/res/values-ta/strings.xml
index 9d04e9a..cd97aa3 100644
--- a/go/quickstep/res/values-ta/strings.xml
+++ b/go/quickstep/res/values-ta/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"ஆப்ஸைப் பகிருங்கள்"</string>
     <string name="action_listen" msgid="2370304050784689486">"கேளுங்கள்"</string>
     <string name="action_translate" msgid="8028378961867277746">"மொழிபெயர்"</string>
-    <string name="action_search" msgid="6269564710943755464">"லென்ஸ்"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"சரி"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"ரத்துசெய்"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"அமைப்புகள்"</string>
diff --git a/go/quickstep/res/values-zu/strings.xml b/go/quickstep/res/values-zu/strings.xml
index d496b64..1be7898 100644
--- a/go/quickstep/res/values-zu/strings.xml
+++ b/go/quickstep/res/values-zu/strings.xml
@@ -4,7 +4,7 @@
     <string name="app_share_drop_target_label" msgid="5804774105974539508">"Yabelana nge-App"</string>
     <string name="action_listen" msgid="2370304050784689486">"Lalela"</string>
     <string name="action_translate" msgid="8028378961867277746">"Humusha"</string>
-    <string name="action_search" msgid="6269564710943755464">"Ilensi"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"NGIYITHOLILE"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"KHANSELA"</string>
     <string name="dialog_settings" msgid="6564397136021186148">"AMASETHINGI"</string>
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index c559988..a61e430 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -23,6 +23,8 @@
 //
 // ItemInfos
 message ItemInfo {
+  reserved 8;
+
   oneof Item {
     Application application = 1;
     Task task = 2;
@@ -42,7 +44,14 @@
   optional ContainerInfo container_info = 7;
 
   // Stores the origin of the Item
-  optional Attribute attribute = 8;
+  repeated Attribute item_attributes = 12;
+}
+
+message LauncherAttributes{
+
+  // Integer value of item attribute enum
+  // (e.g. SUGGESTED_LABEL, ALL_APPS_SEARCH_RESULT_SETTING etc)
+  repeated int32 item_attributes = 1;
 }
 
 // Represents various launcher surface where items are placed.
@@ -156,6 +165,13 @@
   ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30;
   ALL_APPS_SEARCH_RESULT_ASSISTANT_MEMORY = 31;
 
+  // Suggestion Type provided by AGA
+  ONE_SEARCH_WEB_QUERY = 32;
+  ONE_SEARCH_WEB_TRENDING = 33;
+  ONE_SEARCH_WEB_ENTITY = 34;
+  ONE_SEARCH_WEB_ANSWER = 35;
+  ONE_SEARCH_WEB_PERSONAL = 36;
+
   WIDGETS_BOTTOM_TRAY = 28;
   WIDGETS_TRAY_PREDICTION = 29;
 }
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
new file mode 100644
index 0000000..e5f0295
--- /dev/null
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.testing.DebugTestInformationHandler;
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
+ */
+public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
+
+    private final DebugTestInformationHandler mDebugTestInformationHandler;
+
+    public DebugQuickstepTestInformationHandler(Context context) {
+        super(context);
+        mDebugTestInformationHandler = new DebugTestInformationHandler(context);
+    }
+
+    @Override
+    public Bundle call(String method, String arg, @Nullable Bundle extras) {
+        Bundle response = new Bundle();
+        switch (method) {
+            case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
+                runOnUIThread(l -> {
+                    enableManualTaskbarStashing(l, true);
+                });
+                return response;
+
+            case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
+                runOnUIThread(l -> {
+                    enableManualTaskbarStashing(l, false);
+                });
+                return response;
+
+            case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
+                runOnUIThread(l -> {
+                    enableManualTaskbarStashing(l, true);
+
+                    BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) l;
+                    LauncherTaskbarUIController taskbarUIController =
+                            quickstepLauncher.getTaskbarUIController();
+
+                    // Allow null-pointer to catch illegal states.
+                    taskbarUIController.unstashTaskbarIfStashed();
+
+                    enableManualTaskbarStashing(l, false);
+                });
+                return response;
+
+            case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
+                final Resources resources = mContext.getResources();
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                        resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
+                return response;
+            }
+
+            default:
+                response = super.call(method, arg, extras);
+                if (response != null) return response;
+                return mDebugTestInformationHandler.call(method, arg, extras);
+        }
+    }
+
+    private void enableManualTaskbarStashing(Launcher launcher, boolean enable) {
+        BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) launcher;
+        LauncherTaskbarUIController taskbarUIController =
+                quickstepLauncher.getTaskbarUIController();
+
+        // Allow null-pointer to catch illegal states.
+        taskbarUIController.enableManualStashingForTests(enable);
+    }
+
+    /**
+     * Runs the given command on the UI thread.
+     */
+    private static void runOnUIThread(UIThreadCommand command) {
+        try {
+            MAIN_EXECUTOR.submit(() -> {
+                command.execute(Launcher.ACTIVITY_TRACKER.getCreatedActivity());
+                return null;
+            }).get();
+        } catch (ExecutionException | InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private interface UIThreadCommand {
+
+        void execute(Launcher launcher);
+    }
+}
+
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
index ff050ea..a1566f0 100644
--- a/quickstep/protos_overrides/launcher_atom_extension.proto
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -42,5 +42,18 @@
 
     // True if the item's title/content is a direct match to the search query, false otherwise.
     optional bool direct_match = 2;
+
+    // Entry point for this on-device search session
+    optional EntryState entry_state = 3;
+
+    enum EntryState{
+      ENTRY_STATE_UNKNOWN = 0;
+
+      // User entered using swipe-up gesture from homescreen and searchbox in AllApps drawer.
+      ALL_APPS = 1;
+
+      // User entered by tapping on QSB bar on homescreen.
+      QSB = 2;
+    }
   }
 }
diff --git a/quickstep/res/drawable-v28/gesture_tutorial_action_button_background.xml b/quickstep/res/drawable-v28/gesture_tutorial_action_button_background.xml
index 57423c2..710482f 100644
--- a/quickstep/res/drawable-v28/gesture_tutorial_action_button_background.xml
+++ b/quickstep/res/drawable-v28/gesture_tutorial_action_button_background.xml
@@ -16,5 +16,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
     <corners android:radius="?android:attr/dialogCornerRadius"/>
-    <solid android:color="@color/gesture_tutorial_primary_color"/>
+    <solid android:color="?android:attr/colorAccent"/>
 </shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/bg_sandbox_feedback.xml b/quickstep/res/drawable/bg_sandbox_feedback.xml
index 83a3dea..83d7e43 100644
--- a/quickstep/res/drawable/bg_sandbox_feedback.xml
+++ b/quickstep/res/drawable/bg_sandbox_feedback.xml
@@ -14,7 +14,8 @@
     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">
   <corners android:radius="28dp"/>
-  <solid android:color="?android:attr/colorBackgroundFloating"/>
+  <solid android:color="?androidprv:attr/colorSurface"/>
 </shape>
diff --git a/quickstep/res/drawable/gesture_tutorial_action_button_background.xml b/quickstep/res/drawable/gesture_tutorial_action_button_background.xml
index ac6a52a..98dc1a5 100644
--- a/quickstep/res/drawable/gesture_tutorial_action_button_background.xml
+++ b/quickstep/res/drawable/gesture_tutorial_action_button_background.xml
@@ -25,7 +25,7 @@
         <shape
             android:shape="rectangle">
             <corners android:radius="50dp"/>
-            <solid android:color="@color/gesture_tutorial_primary_color"/>
+            <solid android:color="?android:attr/colorAccent"/>
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml b/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml
index 0a34af6..7762615 100644
--- a/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml
+++ b/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml
@@ -17,5 +17,5 @@
     android:shape="rectangle">
     <corners android:radius="50dp"/>
     <solid android:color="@android:color/transparent"/>
-    <stroke android:width="1dp" android:color="@color/gesture_tutorial_primary_color"/>
+    <stroke android:width="1dp" android:color="?android:attr/colorAccent"/>
 </shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_finger_dot.xml b/quickstep/res/drawable/gesture_tutorial_finger_dot.xml
index 5f8aafd..cbb2612 100644
--- a/quickstep/res/drawable/gesture_tutorial_finger_dot.xml
+++ b/quickstep/res/drawable/gesture_tutorial_finger_dot.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
-    <solid android:color="@color/gesture_tutorial_primary_color" />
+    <solid android:color="?android:attr/colorAccent" />
     <size android:width="92dp" android:height="92dp"/>
 </shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_loop_back.xml b/quickstep/res/drawable/gesture_tutorial_loop_back.xml
index d2909ff..ae47709 100644
--- a/quickstep/res/drawable/gesture_tutorial_loop_back.xml
+++ b/quickstep/res/drawable/gesture_tutorial_loop_back.xml
@@ -85,7 +85,7 @@
                     <path
                         android:name="_R_G_L_0_G_D_0_P_0"
                         android:fillAlpha="0.25"
-                        android:fillColor="@color/gesture_tutorial_primary_color"
+                        android:fillColor="?android:attr/colorAccent"
                         android:fillType="nonZero"
                         android:pathData=" M12.5 -446 C12.5,-446 12.5,446 12.5,446 C12.5,446 -12.5,446 -12.5,446 C-12.5,446 -12.5,-446 -12.5,-446 C-12.5,-446 12.5,-446 12.5,-446c " />
                 </group>
diff --git a/quickstep/res/drawable/gesture_tutorial_loop_home.xml b/quickstep/res/drawable/gesture_tutorial_loop_home.xml
index 931f8c0..bed35dd 100644
--- a/quickstep/res/drawable/gesture_tutorial_loop_home.xml
+++ b/quickstep/res/drawable/gesture_tutorial_loop_home.xml
@@ -81,7 +81,7 @@
                     <path
                         android:name="_R_G_L_1_G_D_0_P_0"
                         android:fillAlpha="0.25"
-                        android:fillColor="@color/gesture_tutorial_primary_color"
+                        android:fillColor="?android:attr/colorAccent"
                         android:fillType="nonZero"
                         android:pathData=" M206 -12.5 C206,-12.5 206,12.5 206,12.5 C206,12.5 -206,12.5 -206,12.5 C-206,12.5 -206,-12.5 -206,-12.5 C-206,-12.5 206,-12.5 206,-12.5c " />
                 </group>
diff --git a/quickstep/res/drawable/gesture_tutorial_loop_overview.xml b/quickstep/res/drawable/gesture_tutorial_loop_overview.xml
index a4c532b..53e8b5f 100644
--- a/quickstep/res/drawable/gesture_tutorial_loop_overview.xml
+++ b/quickstep/res/drawable/gesture_tutorial_loop_overview.xml
@@ -81,7 +81,7 @@
                     <path
                         android:name="_R_G_L_1_G_D_0_P_0"
                         android:fillAlpha="0.25"
-                        android:fillColor="@color/gesture_tutorial_primary_color"
+                        android:fillColor="?android:attr/colorAccent"
                         android:fillType="nonZero"
                         android:pathData=" M206 -12.5 C206,-12.5 206,12.5 206,12.5 C206,12.5 -206,12.5 -206,12.5 C-206,12.5 -206,-12.5 -206,-12.5 C-206,-12.5 206,-12.5 206,-12.5c " />
                 </group>
diff --git a/quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml b/quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml
new file mode 100644
index 0000000..20d2ecc
--- /dev/null
+++ b/quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml
@@ -0,0 +1,80 @@
+<?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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:paddingTop="26dp"
+    android:paddingBottom="26dp"
+    android:paddingStart="56dp"
+    android:paddingEnd="56dp">
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_1"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintVertical_chainStyle="spread_inside"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/hotseat_icon_2"
+        app:layout_constraintStart_toStartOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_2"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_icon_1"
+        app:layout_constraintBottom_toTopOf="@id/hotseat_icon_3"
+        app:layout_constraintStart_toStartOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_3"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_3"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_icon_2"
+        app:layout_constraintBottom_toTopOf="@id/hotseat_icon_4"
+        app:layout_constraintStart_toStartOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_4"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_4"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_icon_3"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml b/quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml
new file mode 100644
index 0000000..6877b89
--- /dev/null
+++ b/quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml
@@ -0,0 +1,123 @@
+<?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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="32dp"
+    android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end"
+    android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end">
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_search_bar"
+        android:layout_width="200dp"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
+
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
+        app:cardBackgroundColor="@color/mock_search_bar"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_1"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_1"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_search_bar"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_2"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_2"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_1"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_3"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_3"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_3"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_2"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_4"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_4"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_5"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_5"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_4"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_4"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_6"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_6"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_5"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 9ad10dc..06dfa37 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -60,7 +60,7 @@
 
             <TextView
                 android:id="@+id/title"
-                style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+                style="@style/TextAppearance.GestureTutorial.Feedback.Title.AllSet"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/allset_title_margin_top"
@@ -71,15 +71,14 @@
 
             <TextView
                 android:id="@+id/subtitle"
-                style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+                style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle.AllSet"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/allset_subtitle_margin_top"
                 app:layout_constraintTop_toBottomOf="@id/title"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
-                android:gravity="start"
-                android:text="@string/allset_description"/>
+                android:gravity="start"/>
 
             <androidx.constraintlayout.widget.Guideline
                 android:id="@+id/navigation_settings_guideline_bottom"
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
index 5612666..027e4a0 100644
--- a/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
@@ -1,12 +1,26 @@
 <?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.
+-->
 <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingBottom="32dp"
-    android:paddingStart="170dp"
-    android:paddingEnd="170dp">
+    android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end"
+    android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end">
 
     <androidx.cardview.widget.CardView
         android:id="@+id/hotseat_search_bar"
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 08e6178..b3ca297 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -27,10 +27,8 @@
 
         <FrameLayout
             android:id="@+id/gesture_tutorial_fake_hotseat_view"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerHorizontal="true"
-            android:layout_alignParentBottom="true"/>
+            android:layout_width="@dimen/gesture_tutorial_hotseat_width"
+            android:layout_height="@dimen/gesture_tutorial_hotseat_height"/>
 
     </RelativeLayout>
 
@@ -102,7 +100,7 @@
         android:background="@drawable/gesture_tutorial_ripple"/>
 
     <include
-        layout="@layout/gesture_tutorial_foldable_mock_taskbar"
+        layout="@layout/gesture_tutorial_tablet_mock_taskbar"
         android:id="@+id/gesture_tutorial_fake_taskbar_view"
         android:layout_width="match_parent"
         android:layout_height="@dimen/gesture_tutorial_mock_taskbar_height"
@@ -121,33 +119,23 @@
         android:scaleType="fitXY"
         android:visibility="gone"/>
 
-    <ImageView
-        android:id="@+id/gesture_tutorial_finger_dot"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/gesture_tutorial_finger_dot"
-        android:layout_centerHorizontal="true"
-        android:layout_centerVertical="true"
-        android:visibility="gone"/>
-
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/gesture_tutorial_fragment_feedback_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         android:layout_centerHorizontal="true"
-        android:layout_marginTop="24dp"
         android:paddingTop="24dp"
         android:paddingBottom="16dp"
+        android:paddingStart="24dp"
+        android:paddingEnd="24dp"
         android:background="@drawable/bg_sandbox_feedback">
 
         <TextView
             android:id="@+id/gesture_tutorial_fragment_feedback_title"
             style="@style/TextAppearance.GestureTutorial.Feedback.Title"
-            android:layout_width="0dp"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="24dp"
-            android:layout_marginEnd="24dp"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
@@ -158,9 +146,7 @@
             style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
-            android:layout_marginTop="24dp"
-            android:layout_marginStart="24dp"
-            android:layout_marginEnd="24dp"
+            android:layout_marginTop="16dp"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
@@ -182,7 +168,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="32dp"
-            android:layout_marginEnd="16dp"
             android:paddingTop="16dp"
             android:paddingBottom="16dp"
             android:paddingStart="26dp"
@@ -201,11 +186,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="32dp"
-            android:layout_marginEnd="16dp"
             android:paddingTop="16dp"
             android:paddingBottom="16dp"
-            android:paddingStart="26dp"
-            android:paddingEnd="26dp"
             android:text="@string/gesture_tutorial_action_button_label_skip"
             android:background="?android:attr/selectableItemBackgroundBorderless"
 
@@ -214,4 +196,13 @@
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 
+    <ImageView
+        android:id="@+id/gesture_tutorial_finger_dot"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/gesture_tutorial_finger_dot"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        android:visibility="gone"/>
+
 </com.android.quickstep.interaction.RootSandboxLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
index e8d5d79..5550389 100644
--- a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
+++ b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
@@ -34,8 +34,8 @@
             android:layout_height="0dp"
             android:layout_marginTop="43dp"
             android:layout_marginBottom="22dp"
-            android:layout_marginStart="34dp"
-            android:layout_marginEnd="211dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_top_bar_margin_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_top_bar_margin_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="4dp"
@@ -84,7 +84,7 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:background="@color/mock_conversation_background"
-        android:paddingBottom="66dp"
+        android:paddingBottom="@dimen/gesture_tutorial_conversation_bottom_padding"
 
         app:layout_constraintTop_toBottomOf="@id/top_bar"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -108,6 +108,7 @@
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
                 android:layout_marginStart="124dp"
                 android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
+                android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -122,6 +123,7 @@
                 android:layout_height="@dimen/gesture_tutorial_message_icon_size"
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
                 android:layout_marginStart="@dimen/gesture_tutorial_message_padding_start"
+                android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
@@ -135,6 +137,7 @@
                 android:layout_height="36dp"
                 android:layout_marginStart="17dp"
                 android:layout_marginEnd="112dp"
+                android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -151,6 +154,7 @@
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
                 android:layout_marginStart="280dp"
                 android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
+                android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -164,7 +168,7 @@
                 android:layout_width="0dp"
                 android:layout_height="74dp"
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
-                android:layout_marginStart="124dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_message_margin_start"
                 android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
 
                 app:cardElevation="0dp"
@@ -192,7 +196,7 @@
                 android:layout_width="0dp"
                 android:layout_height="36dp"
                 android:layout_marginStart="17dp"
-                android:layout_marginEnd="144dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_reply_margin_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -206,7 +210,7 @@
                 android:id="@+id/message_4"
                 android:layout_width="0dp"
                 android:layout_height="74dp"
-                android:layout_marginStart="124dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_message_margin_start"
                 android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
 
                 app:cardElevation="0dp"
diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
index 364ad6d..a172ad3 100644
--- a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
+++ b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
@@ -35,7 +35,7 @@
             android:layout_marginTop="43dp"
             android:layout_marginBottom="22dp"
             android:layout_marginStart="34dp"
-            android:layout_marginEnd="35dp"
+            android:layout_marginEnd="34dp"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="4dp"
@@ -51,337 +51,336 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:background="@color/mock_list_background"
-        android:paddingBottom="66dp"
+        android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
+        android:paddingStart="26dp"
 
         app:layout_constraintTop_toBottomOf="@id/top_bar"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent">
 
-        <androidx.constraintlayout.widget.ConstraintLayout
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
-            android:paddingStart="26dp"
-            android:paddingBottom="14dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_1"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
 
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
             app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toTopOf="@id/mock_button">
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_1"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_1"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_1_margin_end"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toTopOf="parent"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_1"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_1"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="217dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_2"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_2_margin_end"
+            android:layout_marginTop="4dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_1"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_1"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_2"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="142dp"
-                android:layout_marginTop="4dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_2"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_marginTop="32dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_1"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
+            app:layout_constraintTop_toBottomOf="@id/conversation_icon_1"
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_2"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_marginTop="32dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_3"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_3_margin_end"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toBottomOf="@id/conversation_icon_1"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_2"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_3"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="190dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_4"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_4_margin_end"
+            android:layout_marginTop="4dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_2"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_3"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_4"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="171dp"
-                android:layout_marginTop="4dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_3"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_marginTop="32dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_3"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
+            app:layout_constraintTop_toBottomOf="@id/conversation_icon_2"
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_3"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_marginTop="32dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_5"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_5_margin_end"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toBottomOf="@id/conversation_icon_2"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_3"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_5"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="198dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_6"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_6_margin_end"
+            android:layout_marginTop="4dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_3"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_5"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_6"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="79dp"
-                android:layout_marginTop="4dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_4"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_marginTop="32dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_5"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
+            app:layout_constraintTop_toBottomOf="@id/conversation_icon_3"
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_4"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_marginTop="32dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_7"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_7_margin_end"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toBottomOf="@id/conversation_icon_3"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_4"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_7"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="174dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_8"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_8_margin_end"
+            android:layout_marginTop="4dp"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_4"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_7"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_8"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="117dp"
-                android:layout_marginTop="4dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_5"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_marginTop="32dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_7"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
+            app:layout_constraintTop_toBottomOf="@id/conversation_icon_4"
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_5"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_marginTop="32dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_9"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="244dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toBottomOf="@id/conversation_icon_4"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_5"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_9"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="244dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_10"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="143dp"
+            android:layout_marginTop="4dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_5"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_9"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_10"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="143dp"
-                android:layout_marginTop="4dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_6"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_marginTop="32dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_9"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
+            app:layout_constraintTop_toBottomOf="@id/conversation_icon_5"
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_6"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_marginTop="32dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_11"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="177dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toBottomOf="@id/conversation_icon_5"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_6"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_11"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="177dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_12"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="117dp"
+            android:layout_marginTop="4dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_6"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_11"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_12"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="117dp"
-                android:layout_marginTop="4dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_icon_7"
+            android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
+            android:layout_marginTop="32dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_11"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_list_profile_icon"
+            app:layout_constraintTop_toBottomOf="@id/conversation_icon_6"
+            app:layout_constraintStart_toStartOf="parent"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_icon_7"
-                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
-                android:layout_marginTop="32dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_13"
+            android:layout_width="0dp"
+            android:layout_height="18dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="189dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
-                app:cardBackgroundColor="@color/mock_list_profile_icon"
-                app:layout_constraintTop_toBottomOf="@id/conversation_icon_6"
-                app:layout_constraintStart_toStartOf="parent"/>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:layout_constraintTop_toTopOf="@id/conversation_icon_7"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/>
 
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_13"
-                android:layout_width="0dp"
-                android:layout_height="18dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="189dp"
+        <androidx.cardview.widget.CardView
+            android:id="@+id/conversation_line_14"
+            android:layout_width="0dp"
+            android:layout_height="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+            android:layout_marginEnd="166dp"
+            android:layout_marginTop="4dp"
+            android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
 
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintVertical_chainStyle="packed"
-                app:layout_constraintTop_toTopOf="@id/conversation_icon_7"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/>
-
-            <androidx.cardview.widget.CardView
-                android:id="@+id/conversation_line_14"
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="166dp"
-                android:layout_marginTop="4dp"
-
-                app:cardElevation="0dp"
-                app:cardCornerRadius="4dp"
-                app:cardBackgroundColor="@color/mock_list_preview_message"
-                app:layout_constraintTop_toBottomOf="@id/conversation_line_13"
-                app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
-
-        </androidx.constraintlayout.widget.ConstraintLayout>
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_preview_message"
+            app:layout_constraintTop_toBottomOf="@id/conversation_line_13"
+            app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
 
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_button"
             android:layout_width="149dp"
             android:layout_height="56dp"
-            android:layout_marginEnd="24dp"
+            android:layout_marginEnd="@dimen/gesture_tutorial_mock_button_margin_end"
+            android:layout_marginBottom="@dimen/gesture_tutorial_mock_button_margin_bottom"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="164dp"
diff --git a/quickstep/res/layout/gesture_tutorial_mock_webpage.xml b/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
index bb20968..0b1b40d 100644
--- a/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
+++ b/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
@@ -34,8 +34,8 @@
             android:layout_height="0dp"
             android:layout_marginTop="48dp"
             android:layout_marginBottom="16dp"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="16dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_webpage_url_margin_start_end"
+            android:layout_marginEnd="@dimen/gesture_tutorial_webpage_url_margin_start_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="100dp"
@@ -63,7 +63,7 @@
             android:layout_height="0dp"
             android:layout_marginTop="22dp"
             android:layout_marginBottom="22dp"
-            android:layout_marginStart="24dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_webpage_top_bar_button_margin_start"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="8dp"
@@ -78,8 +78,8 @@
             android:layout_height="0dp"
             android:layout_marginTop="28dp"
             android:layout_marginBottom="28dp"
-            android:layout_marginStart="97dp"
-            android:layout_marginEnd="97dp"
+            android:layout_marginStart="@dimen/gesture_tutorial_webpage_top_bar_margin_start"
+            android:layout_marginEnd="@dimen/gesture_tutorial_webpage_top_bar_margin_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="2dp"
@@ -107,7 +107,7 @@
             android:id="@+id/mock_line_1"
             android:layout_width="0dp"
             android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
-            android:layout_marginEnd="126dp"
+            android:layout_marginEnd="@dimen/gesture_tutorial_webpage_line_1_margin_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
@@ -121,7 +121,7 @@
             android:layout_width="0dp"
             android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
-            android:layout_marginEnd="64dp"
+            android:layout_marginEnd="@dimen/gesture_tutorial_webpage_line_2_margin_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
@@ -135,7 +135,7 @@
             android:layout_width="0dp"
             android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
-            android:layout_marginEnd="151dp"
+            android:layout_marginEnd="@dimen/gesture_tutorial_webpage_line_3_margin_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
@@ -174,7 +174,7 @@
             android:layout_width="0dp"
             android:layout_height="240dp"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
-            android:layout_marginEnd="24dp"
+            android:layout_marginEnd="@dimen/gesture_tutorial_webpage_block_margin_end"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_large_corner_radius"
@@ -189,6 +189,7 @@
             android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
             android:layout_marginEnd="52dp"
+            android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -203,6 +204,7 @@
             android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="41dp"
+            android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -217,6 +219,7 @@
             android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="71dp"
+            android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -231,6 +234,7 @@
             android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="198dp"
+            android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -245,6 +249,7 @@
             android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
             android:layout_marginEnd="64dp"
+            android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -258,6 +263,7 @@
             android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
             android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="71dp"
+            android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_conversation.xml
similarity index 93%
rename from quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
rename to quickstep/res/layout/gesture_tutorial_tablet_mock_conversation.xml
index b0cc00b..c8cf8c3 100644
--- a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
+++ b/quickstep/res/layout/gesture_tutorial_tablet_mock_conversation.xml
@@ -106,8 +106,8 @@
                 android:layout_width="0dp"
                 android:layout_height="112dp"
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
-                android:layout_marginStart="445dp"
-                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+                android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_1_margin"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -121,7 +121,7 @@
                 android:layout_width="@dimen/gesture_tutorial_message_icon_size"
                 android:layout_height="@dimen/gesture_tutorial_message_icon_size"
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
-                android:layout_marginStart="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+                android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_padding_start_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
@@ -134,7 +134,7 @@
                 android:layout_width="0dp"
                 android:layout_height="36dp"
                 android:layout_marginStart="17dp"
-                android:layout_marginEnd="441dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_reply_1_margin"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -149,8 +149,8 @@
                 android:layout_width="0dp"
                 android:layout_height="36dp"
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
-                android:layout_marginStart="601dp"
-                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+                android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_2_margin"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -164,8 +164,8 @@
                 android:layout_width="0dp"
                 android:layout_height="74dp"
                 android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
-                android:layout_marginStart="445dp"
-                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+                android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_3_margin"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -179,7 +179,7 @@
                 android:layout_width="@dimen/gesture_tutorial_message_icon_size"
                 android:layout_height="@dimen/gesture_tutorial_message_icon_size"
                 android:layout_marginBottom="32dp"
-                android:layout_marginStart="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+                android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_padding_start_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
@@ -192,7 +192,7 @@
                 android:layout_width="0dp"
                 android:layout_height="36dp"
                 android:layout_marginStart="17dp"
-                android:layout_marginEnd="473dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_reply_2_margin"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -206,8 +206,8 @@
                 android:id="@+id/message_4"
                 android:layout_width="0dp"
                 android:layout_height="74dp"
-                android:layout_marginStart="445dp"
-                android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
+                android:layout_marginStart="345dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_conversation_list.xml
similarity index 95%
rename from quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
rename to quickstep/res/layout/gesture_tutorial_tablet_mock_conversation_list.xml
index e5cd9bc..0fb0677 100644
--- a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
+++ b/quickstep/res/layout/gesture_tutorial_tablet_mock_conversation_list.xml
@@ -67,7 +67,7 @@
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/mock_button">
+            app:layout_constraintEnd_toEndOf="parent">
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_1"
@@ -189,7 +189,7 @@
                 android:layout_width="0dp"
                 android:layout_height="16dp"
                 android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="15dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_conversation_line_6_margin_end"
                 android:layout_marginTop="4dp"
 
                 app:cardElevation="0dp"
@@ -233,7 +233,7 @@
                 android:layout_width="0dp"
                 android:layout_height="16dp"
                 android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="72dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_conversation_line_8_margin_end"
                 android:layout_marginTop="4dp"
 
                 app:cardElevation="0dp"
@@ -277,7 +277,7 @@
                 android:layout_width="0dp"
                 android:layout_height="16dp"
                 android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
-                android:layout_marginEnd="111dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_tablet_conversation_line_10_margin_end"
                 android:layout_marginTop="4dp"
 
                 app:cardElevation="0dp"
@@ -376,21 +376,21 @@
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
 
+            <androidx.cardview.widget.CardView
+                android:id="@+id/mock_button"
+                android:layout_width="149dp"
+                android:layout_height="56dp"
+                android:layout_marginEnd="126dp"
+                android:layout_marginBottom="24dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="164dp"
+                app:cardBackgroundColor="@color/mock_list_button"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
         </androidx.constraintlayout.widget.ConstraintLayout>
 
-        <androidx.cardview.widget.CardView
-            android:id="@+id/mock_button"
-            android:layout_width="149dp"
-            android:layout_height="56dp"
-            android:layout_marginEnd="126dp"
-            android:layout_marginBottom="24dp"
-
-            app:cardElevation="0dp"
-            app:cardCornerRadius="164dp"
-            app:cardBackgroundColor="@color/mock_list_button"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"/>
-
     </androidx.constraintlayout.widget.ConstraintLayout>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml
new file mode 100644
index 0000000..027e4a0
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml
@@ -0,0 +1,122 @@
+<?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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="32dp"
+    android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end"
+    android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end">
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_search_bar"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
+        app:cardBackgroundColor="@color/mock_search_bar"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_1"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_2"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_2"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_1"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_3"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_3"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_3"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_2"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_4"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_4"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_1"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_5"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_5"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_4"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_4"
+        app:layout_constraintEnd_toStartOf="@id/hotseat_icon_6"/>
+
+    <androidx.cardview.widget.CardView
+        android:id="@+id/hotseat_icon_6"
+        android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
+        app:cardBackgroundColor="@color/mock_app_icon_2"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_5"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_taskbar.xml
similarity index 100%
rename from quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml
rename to quickstep/res/layout/gesture_tutorial_tablet_mock_taskbar.xml
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_webpage.xml
similarity index 100%
rename from quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml
rename to quickstep/res/layout/gesture_tutorial_tablet_mock_webpage.xml
diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml
index 7dc0cbe..d402469 100644
--- a/quickstep/res/layout/taskbar_all_apps.xml
+++ b/quickstep/res/layout/taskbar_all_apps.xml
@@ -51,6 +51,12 @@
             <include layout="@layout/all_apps_personal_work_tabs" />
         </com.android.launcher3.allapps.FloatingHeaderView>
 
+        <com.android.launcher3.taskbar.allapps.TaskbarAllAppsFallbackSearchContainer
+            android:id="@+id/search_container_all_apps"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:visibility="gone" />
+
         <include layout="@layout/all_apps_fast_scroller" />
     </com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView>
 </com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index ac10cc8..1b3f1cd 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Gereed!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swiep op om na die tuisskerm toe te gaan"</string>
     <string name="allset_description" msgid="6350320429953234580">"Jy is gereed om jou foon te begin gebruik"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Jy is gereed om jou tablet te begin gebruik"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stelselnavigasie-instellings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deel"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index dfa8532..6daa895 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"ሁሉም ዝግጁ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ወደ መነሻ ለመሄድ በጣት ወደ ላይ ማንሸራተት"</string>
     <string name="allset_description" msgid="6350320429953234580">"ስልክዎን መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ጡባዊዎን መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"የስርዓት አሰሳ ቅንብሮች"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"አጋራ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገጽ እይታ"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 58b26d1..fe9af18 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"اكتملت عملية الإعداد"</string>
     <string name="allset_hint" msgid="2384632994739392447">"مرِّر سريعًا للأعلى للانتقال إلى الشاشة الرئيسية."</string>
     <string name="allset_description" msgid="6350320429953234580">"يمكنك الآن بدء استخدام هاتفك."</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"يمكنك الآن بدء استخدام جهازك اللوحي."</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"إعدادات التنقّل داخل النظام"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"مشاركة"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"رجوع"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"‏مفتاح التبديل إلى IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"الأحدث"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"الإشعارات"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"إعدادات سريعة"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 38a506f..a6a7e38 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"সকলো সাজু!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"গৃহ স্ক্ৰীনলৈ যাবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="allset_description" msgid="6350320429953234580">"আপুনি আপোনাৰ ফ’নটো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"আপুনি আপোনাৰ টেবলেটটো ব্যৱহাৰ কৰিবলৈ সাজু"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ছিষ্টেম নেভিগেশ্বনৰ ছেটিং"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"শ্বেয়াৰ কৰক"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"স্ক্ৰীনশ্বট"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"উভতি যাওক"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ছুইচ্চাৰ"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"শেহতীয়া"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"জাননী"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ক্ষিপ্ৰ ছেটিং"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 74fcb63..3650658 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Hər şey hazırdır!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Əsas səhifəyə keçmək üçün yuxarı çəkin"</string>
     <string name="allset_description" msgid="6350320429953234580">"Telefondan istifadəyə başlamağa hazırsınız"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Planşetdən istifadəyə başlamağa hazırsınız"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem naviqasiya ayarları"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Paylaşın"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skrinşot"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Geriyə"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME keçiricisi"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Sonuncular"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirişlər"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Sürətli Ayarlar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index f495b00..6af2f1f 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Gotovo!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Prevucite nagore da biste otvorili početni ekran"</string>
     <string name="allset_description" msgid="6350320429953234580">"Spremni ste da počnete da koristite telefon"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Spremni ste da počnete da koristite tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Podešavanja kretanja kroz sistem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 4872f7d..a4cdfdc 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Гатова!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Каб перайсці на галоўны экран, правядзіце пальцам уверх"</string>
     <string name="allset_description" msgid="6350320429953234580">"Вы можаце пачаць карыстанне тэлефонам"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Вы можаце пачаць карыстанне планшэтам"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Налады навігацыі ў сістэме"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Абагуліць"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Назад"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Выключальнік IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Нядаўнія"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Апавяшчэнні"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Хуткія налады"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 3fea5ee..e5eedbb 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Прекарайте пръст нагоре, за да отворите началния екран"</string>
     <string name="allset_description" msgid="6350320429953234580">"Можете да започнете да използвате телефона си"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Можете да започнете да използвате таблета си"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Настройки за навигиране в системата"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Споделяне"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Назад"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Редактор за метода на въвежд.: Превключвател"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Скорошни"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Известия"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Бързи настройки"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
 </resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index ddd0edd..1d9d3d4 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"সব রেডি!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"হোম স্ক্রিনে যেতে উপরের দিকে সোয়াইপ করুন"</string>
     <string name="allset_description" msgid="6350320429953234580">"এবারে আপনি ফোন ব্যবহার করতে পারবেন"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"এবারে আপনি ট্যাবলেট ব্যবহার করতে পারবেন"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"সিস্টেম নেভিগেশন সেটিংস"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"শেয়ার করুন"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"স্ক্রিনশট নিন"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 2cf2ef4..5906ac8 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Sve je spremno!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Prevucite prema gore da odete na početnu stranicu"</string>
     <string name="allset_description" msgid="6350320429953234580">"Sve je spremno da počnete koristiti telefon"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Sve je spremno da počnete koristiti tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigiranja sistemom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Dijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 0f6c486..da445ab 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tot a punt!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Llisca cap amunt per anar a la pàgina d\'inici"</string>
     <string name="allset_description" msgid="6350320429953234580">"Ja pots començar a utilitzar el telèfon"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Ja pots començar a utilitzar la tauleta"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuració de navegació del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Comparteix"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Enrere"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Selector d\'IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificacions"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. ràpida"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 6aef1a4..83c92bd 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Hotovo!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Přejetím nahoru se vrátíte na plochu"</string>
     <string name="allset_description" msgid="6350320429953234580">"Jste připraveni začít používat telefon"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Jste připraveni začít používat tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavení navigace v systému"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Sdílet"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímek obrazovky"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Zpět"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Přepínač IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Poslední"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Oznámení"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rychlé nastavení"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index c2e6681..fe069d7 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Alt er parat!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Stryg opad for at gå til startsiden"</string>
     <string name="allset_description" msgid="6350320429953234580">"Du er klar til at bruge din telefon"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Du er klar til at bruge din tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Indstillinger for systemnavigation"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tilbage"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-vælger"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Seneste"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifikationer"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kvikmenu"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 8d5aa7a..24a25f5 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Fertig!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Nach oben wischen, um den Startbildschirm aufzurufen"</string>
     <string name="allset_description" msgid="6350320429953234580">"Du kannst dein Smartphone jetzt verwenden"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Du kannst dein Tablet jetzt verwenden"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Teilen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 10c39b4..3c24a13 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Όλα έτοιμα!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Σύρετε προς τα πάνω για μετάβαση στην αρχική οθόνη."</string>
     <string name="allset_description" msgid="6350320429953234580">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το τηλέφωνό σας"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το tablet."</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ρυθμίσεις πλοήγησης συστήματος"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Κοινοποίηση"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Στιγμιότυπο οθόνης"</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index c67f4c9..88a2c67 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Ready!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
     <string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index c67f4c9..88a2c67 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Ready!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
     <string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index c67f4c9..88a2c67 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Ready!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
     <string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index c67f4c9..88a2c67 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Ready!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swipe up to go home"</string>
     <string name="allset_description" msgid="6350320429953234580">"You’re ready to start using your phone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"You’re ready to start using your tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"System navigation settings"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index c3007f0..1ad2874 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎All set!‎‏‎‎‏‎"</string>
     <string name="allset_hint" msgid="2384632994739392447">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎Swipe up to go Home‎‏‎‎‏‎"</string>
     <string name="allset_description" msgid="6350320429953234580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎You’re ready to start using your phone‎‏‎‎‏‎"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎You’re ready to start using your tablet‎‏‎‎‏‎"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎System navigation settings‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="action_share" msgid="2648470652637092375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎Share‎‏‎‎‏‎"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎Screenshot‎‏‎‎‏‎"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 69b6015..5c29ff8 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Todo listo"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Desliza el dedo hacia arriba para ir a la pantalla principal"</string>
     <string name="allset_description" msgid="6350320429953234580">"Ya puedes empezar a usar tu teléfono"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Ya puedes empezar a usar tu tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración de navegación del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index b5dc6e6..7f60601 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"¡Ya está!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Desliza el dedo hacia arriba para ir a la pantalla de inicio"</string>
     <string name="allset_description" msgid="6350320429953234580">"Ya puedes empezar a usar tu teléfono"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Ya puedes empezar a usar tu tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ajustes de navegación del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atrás"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Interruptor IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recientes"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificaciones"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ajustes rápidos"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 950263f..5d88a75 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Valmis!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Avakuvale liikumiseks pühkige üles"</string>
     <string name="allset_description" msgid="6350320429953234580">"Olete valmis oma telefoni kasutama."</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Olete valmis oma tahvelarvutit kasutama"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tagasi"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME vahetaja"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Hiljutised"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Märguanded"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kiirseaded"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 6193e35..077cb02 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Dena prest!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Pasatu hatza gora hasierako pantailara joateko"</string>
     <string name="allset_description" msgid="6350320429953234580">"Prest zaude telefonoa erabiltzen hasteko"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Prest zaude tableta erabiltzen hasteko"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sisteman nabigatzeko ezarpenak"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partekatu"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 2323b3b..380e412 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"همه چیز آماده است!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"برای رفتن به «صفحه اصلی»، تند به‌بالا بکشید"</string>
     <string name="allset_description" msgid="6350320429953234580">"آماده‌اید از تلفنتان استفاده کنید"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"آماده‌اید از رایانه لوحی‌تان استفاده کنید"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"تنظیمات پیمایش سیستم"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"هم‌رسانی"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 9505039..d74cfb7 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Valmis"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Siirry aloitusnäytölle pyyhkäisemällä ylös"</string>
     <string name="allset_description" msgid="6350320429953234580">"Olet valmis aloittamaan puhelimen käytön"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Olet valmis aloittamaan tabletin käytön"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaa"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Takaisin"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-vaihtopalvelu"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Viimeaikaiset"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Ilmoitukset"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Pika-asetukset"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index e34cb5d..447d483 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tout est prêt!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Balayez l\'écran vers le haut pour accéder à l\'écran d\'accueil"</string>
     <string name="allset_description" msgid="6350320429953234580">"Vous êtes maintenant prêt à utiliser votre téléphone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Vous êtes maintenant prêt à utiliser votre tablette"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation du système"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partager"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Retour"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Sélecteur IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Récents"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Paramètres rapides"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 3c28c46..6fd3401 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tout est prêt !"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Balayez l\'écran vers le haut pour revenir à l\'accueil"</string>
     <string name="allset_description" msgid="6350320429953234580">"Vous pouvez maintenant utiliser votre téléphone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Vous pouvez maintenant utiliser votre tablette"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation système"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partager"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Retour"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Sélecteur IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Récents"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Réglages rapides"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 4095f7d..f409e47 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Todo listo"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Pasa o dedo cara arriba para ir á pantalla de inicio"</string>
     <string name="allset_description" msgid="6350320429953234580">"Todo está listo para comezar a utilizar o teléfono"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Todo está listo para comezar a utilizar a tableta"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configuración da navegación do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atrás"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Selector do IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recentes"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificacións"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Configuración rápida"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 6bb100a..c8c488e 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"બધું સેટ થઈ ગયું!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"હોમપેજ પર જવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
     <string name="allset_description" msgid="6350320429953234580">"તમે તમારા ફોનનો ઉપયોગ કરવા માટે તૈયાર છો"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"તમે તમારા ટૅબ્લેટનો ઉપયોગ કરવા માટે તૈયાર છો"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"સિસ્ટમના નૅવિગેશન સેટિંગ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"શેર કરો"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"સ્ક્રીનશૉટ"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 957125a..bfdc7e3 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"हो गया!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"होम स्क्रीन पर जाने के लिए, ऊपर की ओर स्वाइप करें"</string>
     <string name="allset_description" msgid="6350320429953234580">"अब आप अपना फ़ोन इस्तेमाल कर सकते हैं"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"आप टैबलेट को इस्तेमाल करने के लिए तैयार हैं"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेविगेशन सेटिंग"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"शेयर करें"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट लें"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 6c29822..41e7922 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Sve je spremno!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Prijeđite prstom prema gore da biste otvorili početni zaslon"</string>
     <string name="allset_description" msgid="6350320429953234580">"Spremni ste za početak upotrebe telefona"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Spremni ste za početak upotrebe tableta"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigacije sustavom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 5b1aff9..fb90e09 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Kész is!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Felfelé csúsztatva megjelenik a Kezdőképernyő"</string>
     <string name="allset_description" msgid="6350320429953234580">"Készen áll a telefon használatára"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Készen áll a táblagép használatára"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Rendszer-navigációs beállítások"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Megosztás"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Képernyőkép"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Vissza"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-váltó"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Legutóbbiak"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Értesítések"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Gyorsbeállítások"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
 </resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index df73078..1b9d1e5 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Պատրաստ է"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Մատը սահեցրեք վերև՝ հիմնական էկրան անցնելու համար"</string>
     <string name="allset_description" msgid="6350320429953234580">"Դուք արդեն կարող եք օգտագործել ձեր հեռախոսը"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Դուք արդեն կարող եք օգտագործել ձեր պլանշետը"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Նավիգացիայի համակարգային կարգավորումներ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Կիսվել"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Սքրինշոթ անել"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 61bdf31..8a11e92 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Semua siap."</string>
     <string name="allset_hint" msgid="2384632994739392447">"Geser ke atas untuk beralih ke Layar utama"</string>
     <string name="allset_description" msgid="6350320429953234580">"Anda sudah siap untuk mulai menggunakan ponsel"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Anda sudah siap untuk mulai menggunakan tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setelan navigasi sistem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Bagikan"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 1327552..947e7ee 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Allt tilbúið!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Strjúktu upp til að fara á heimaskjáinn"</string>
     <string name="allset_description" msgid="6350320429953234580">"Þú getur byrjað að nota símann"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Þú getur byrjað að nota spjaldtölvuna"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Stillingar kerfisstjórnunar"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deila"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skjámynd"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Til baka"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Breyta innsláttaraðferð"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nýlegt"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Tilkynningar"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Flýtistillingar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index a555636..044da60 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Finito."</string>
     <string name="allset_hint" msgid="2384632994739392447">"Scorri verso l\'alto per andare alla schermata Home"</string>
     <string name="allset_description" msgid="6350320429953234580">"Puoi iniziare a usare il tuo telefono"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Puoi iniziare a usare il tuo tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Impostazioni Navigazione del sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Condividi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Indietro"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Selettore IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recenti"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifiche"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Impostazioni rapide"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index cd97903..29951d0 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"הכול מוכן!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"כדי לעבור לדף הבית, מחליקים כלפי מעלה"</string>
     <string name="allset_description" msgid="6350320429953234580">"הכול מוכן ואפשר להתחיל להשתמש בטלפון"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"הכול מוכן ואפשר להתחיל להשתמש בטאבלט"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"הגדרות הניווט של המערכת"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"שיתוף"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"צילום מסך"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"חזרה"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"‏כלי להחלפת IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"לאחרונה"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"התראות"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"הגדרות מהירות"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 9a4ce01..066bd0f 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"設定完了"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ホームに移動するには上にスワイプします"</string>
     <string name="allset_description" msgid="6350320429953234580">"スマートフォンを使い始めることができます"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"これでタブレットが使えるようになりました"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"システム ナビゲーションの設定"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"共有"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"スクリーンショット"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 2501489..88281ec 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"მზადაა!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"მთავარ გვერდზე გადასასვლელად გადაფურცლეთ ზევით"</string>
     <string name="allset_description" msgid="6350320429953234580">"მზად ხართ ტელეფონის გამოსაყენებლად"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"მზად ხართ ტაბლეტის გამოსაყენებლად"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"სისტემის ნავიგაციის პარამეტრები"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"გაზიარება"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ეკრანის ანაბეჭდი"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 9d3ca2d..199bd2a 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Бәрі дайын!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Негізгі экранға өту үшін жоғары қарай сырғытыңыз."</string>
     <string name="allset_description" msgid="6350320429953234580">"Телефоныңыз пайдалануға дайын."</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Планшетіңіз пайдалануға дайын."</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Навигацияның жүйелік параметрлері"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Бөлісу"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 3295a63..c3ea1b0 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"រួចហើយ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"អូសឡើងលើ ដើម្បី​ទៅកាន់​អេក្រង់ដើម"</string>
     <string name="allset_description" msgid="6350320429953234580">"អ្នក​អាច​ចាប់ផ្ដើម​ប្រើ​ទូរសព្ទ​របស់អ្នក​បានហើយ"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"អ្នកអាចចាប់ផ្ដើមប្រើថេប្លេតរបស់អ្នកបានហើយ"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ការកំណត់​ការរុករក​ប្រព័ន្ធ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ចែករំលែក"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"រូបថតអេក្រង់"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ថយក្រោយ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"ប៊ូតុងប្ដូរ IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ថ្មីៗ"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ការ​ជូនដំណឹង"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ការកំណត់រហ័ស"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 01c7dd5..d882cf0 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"ಎಲ್ಲವೂ ಸಿದ್ಧವಾಗಿದೆ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="allset_description" msgid="6350320429953234580">"ನಿಮ್ಮ ಫೋನ್ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್‌‌ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಶನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 08e5642..9ff90d4 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"설정 완료"</string>
     <string name="allset_hint" msgid="2384632994739392447">"위로 스와이프하여 홈으로 이동"</string>
     <string name="allset_description" msgid="6350320429953234580">"휴대전화를 사용할 준비가 되었습니다."</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"태블릿을 사용할 준비가 되었습니다."</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"시스템 탐색 설정"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"공유"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"스크린샷"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"뒤로"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME 전환기"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"최근 항목"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"알림"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"빠른 설정"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
 </resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index c1ca8b3..b3a6ca4 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Бүттү!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Башкы бетке өтүү үчүн экранды өйдө сүрүңүз"</string>
     <string name="allset_description" msgid="6350320429953234580">"Телефонуңузду колдоно берсеңиз болот"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Планшетиңизди колдоно берсеңиз болот"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Өтүү аракетинин тутумдук жөндөөлөрү"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Бөлүшүү"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 668aea2..f233bde 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -16,4 +16,61 @@
 -->
 <resources>
     <dimen name="overview_task_margin">8dp</dimen>
+
+    <!-- Tips Gesture Tutorial -->
+    <dimen name="gesture_tutorial_feedback_margin_start_end">126dp</dimen>
+    <dimen name="gesture_tutorial_feedback_margin_top">24dp</dimen>
+
+    <!-- Gesture Tutorial mock conversations -->
+    <dimen name="gesture_tutorial_message_padding_start">42dp</dimen>
+    <dimen name="gesture_tutorial_message_padding_end">60dp</dimen>
+    <dimen name="gesture_tutorial_top_bar_margin_start">42dp</dimen>
+    <dimen name="gesture_tutorial_top_bar_margin_end">683dp</dimen>
+    <dimen name="gesture_tutorial_top_bar_button_margin_end">42dp</dimen>
+    <dimen name="gesture_tutorial_conversation_bottom_padding">35dp</dimen>
+    <integer name="gesture_tutorial_extra_messages_visibility">2</integer> <!-- GONE -->
+    <dimen name="gesture_tutorial_message_margin_start">505dp</dimen>
+    <dimen name="gesture_tutorial_reply_margin_end">462dp</dimen>
+    <dimen name="gesture_tutorial_input_margin_start">103dp</dimen>
+    <dimen name="gesture_tutorial_input_margin_end">103dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_1_margin">345dp</dimen>
+    <dimen name="gesture_tutorial_tablet_reply_1_margin">341dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_2_margin">501dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_3_margin">345dp</dimen>
+    <dimen name="gesture_tutorial_tablet_reply_2_margin">373dp</dimen>
+
+    <!-- Gesture Tutorial mock conversation lists -->
+    <dimen name="gesture_tutorial_conversation_line_1_margin_end">607dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_2_margin_end">460dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_3_margin_end">554dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_4_margin_end">517dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_5_margin_end">570dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_6_margin_end">336dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_7_margin_end">523dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_8_margin_end">500dp</dimen>
+    <dimen name="gesture_tutorial_tablet_conversation_line_6_margin_end">15dp</dimen>
+    <dimen name="gesture_tutorial_tablet_conversation_line_8_margin_end">72dp</dimen>
+    <dimen name="gesture_tutorial_tablet_conversation_line_10_margin_end">111dp</dimen>
+    <integer name="gesture_tutorial_extra_conversations_visibility">2</integer> <!-- GONE -->
+    <dimen name="gesture_tutorial_mock_button_margin_end">34dp</dimen>
+    <dimen name="gesture_tutorial_mock_button_margin_bottom">42dp</dimen>
+
+    <!-- Gesture Tutorial mock hotseats -->
+    <dimen name="gesture_tutorial_hotseat_width">-2px</dimen> <!-- wrap_content -->
+    <dimen name="gesture_tutorial_hotseat_height">-1px</dimen> <!-- match_parent -->
+    <dimen name="gesture_tutorial_hotseat_padding_start_end">170dp</dimen>
+
+    <!-- Gesture Tutorial mock webpages -->
+    <dimen name="gesture_tutorial_webpage_url_margin_start_end">24dp</dimen>
+    <dimen name="gesture_tutorial_webpage_top_bar_button_margin_start">48dp</dimen>
+    <dimen name="gesture_tutorial_webpage_top_bar_margin_start">121dp</dimen>
+    <dimen name="gesture_tutorial_webpage_top_bar_margin_end">355dp</dimen>
+    <dimen name="gesture_tutorial_webpage_line_1_margin_end">355dp</dimen>
+    <dimen name="gesture_tutorial_webpage_line_2_margin_end">208dp</dimen>
+    <dimen name="gesture_tutorial_webpage_line_3_margin_end">439dp</dimen>
+    <dimen name="gesture_tutorial_webpage_block_margin_end">311dp</dimen>
+    <integer name="gesture_tutorial_webpage_extra_lines_visibility">2</integer> <!-- GONE -->
+
+    <!-- Gesture Tutorial mock taskbar -->
+    <dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 4c1791f..eb2b835 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"ຮຽບຮ້ອຍໝົດແລ້ວ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ປັດຂຶ້ນເພື່ອໄປຫາໜ້າຫຼັກ"</string>
     <string name="allset_description" msgid="6350320429953234580">"ທ່ານພ້ອມເລີ່ມຕົ້ນໃຊ້ໂທລະສັບຂອງທ່ານແລ້ວ"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ທ່ານພ້ອມເລີ່ມຕົ້ນໃຊ້ແທັບເລັດຂອງທ່ານແລ້ວ"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ການຕັ້ງຄ່າການນຳທາງລະບົບ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ແບ່ງປັນ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ຮູບໜ້າຈໍ"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ກັບຄືນ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"ຕົວສະຫຼັບ IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ຫຼ້າສຸດ"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ການແຈ້ງເຕືອນ"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ການຕັ້ງຄ່າດ່ວນ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 95a7e86..8bf7353 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Paruošta!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Perbraukite aukštyn, kad grįžtumėte į pagrindinį ekraną"</string>
     <string name="allset_description" msgid="6350320429953234580">"Esate pasiruošę pradėti naudoti telefoną"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Esate pasiruošę pradėti naudoti planšetinį kompiuterį"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistemos naršymo nustatymai"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Bendrinti"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekrano kopija"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atgal"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IMRP perjungiklis"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Naujausi"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Pranešimai"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Spartieji nustatymai"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index f3de0b7..12b7afd 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Gatavs!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Velciet augšup, lai pārietu uz sākuma ekrānu."</string>
     <string name="allset_description" msgid="6350320429953234580">"Varat sākt izmantot savu tālruni"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Varat sākt izmantot savu planšetdatoru"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistēmas navigācijas iestatījumi"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Kopīgot"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Veikt ekrānuzņēmumu"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atpakaļ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME pārslēdzējs"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nesenie"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Paziņojumi"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ātrie iestatīj."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
 </resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index dc0732f..fab6b6e 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Повлечете нагоре за да појдете на почетниот екран"</string>
     <string name="allset_description" msgid="6350320429953234580">"Спремни сте да почнете да го користите телефонот"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Спремни сте да почнете да го користите таблетот"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Поставки за системска навигација"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Сподели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 4ac251c..07343f4 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"എല്ലാം സജ്ജീകരിച്ചു!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ഹോമിലേക്ക് പോകാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="allset_description" msgid="6350320429953234580">"ഫോൺ ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങൾ തയ്യാറാണ്"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ടാബ്‌ലെറ്റ് ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങൾ തയ്യാറാണ്"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"സിസ്‌റ്റം നാവിഗേഷൻ ക്രമീകരണം"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"പങ്കിടുക"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 6d918f6..9a35b90 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Тохируулж дууслаа!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Нүүр хуудас руу очихын тулд дээш шударна уу"</string>
     <string name="allset_description" msgid="6350320429953234580">"Та утсаа ашиглаж эхлэхэд бэлэн боллоо"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Та таблетаа ашиглаж эхлэхэд бэлэн боллоо"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системийн навигацын тохиргоо"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Хуваалцах"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Дэлгэцийн агшин дарах"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index aa374b4..d658fe3 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"सर्व तयार आहे!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"होम वर जाण्यासाठी वरती स्वाइप करा"</string>
     <string name="allset_description" msgid="6350320429953234580">"तुम्ही तुमचा फोन वापरण्यास सुरुवात करू शकता"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"तुम्ही तुमचा टॅबलेट वापरण्यास सुरुवात करू शकता"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेव्हिगेशन सेटिंग्ज"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"शेअर करा"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 8b9165d..322f00f 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Siap!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Leret ke atas untuk kembali ke Laman Utama"</string>
     <string name="allset_description" msgid="6350320429953234580">"Anda sudah sedia untuk mula menggunakan telefon anda"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Anda bersedia untuk mula menggunakan tablet anda"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tetapan navigasi sistem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Kongsi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index d09165a..df9101e 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"အားလုံး အဆင်သင့်ပါ။"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ပင်မစာမျက်နှာသို့သွားရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
     <string name="allset_description" msgid="6350320429953234580">"သင့်ဖုန်းကို စတင်အသုံးပြုရန် အသင့်ဖြစ်ပါပြီ"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"သင့်တက်ဘလက်ကို စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"နောက်သို့"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ပြောင်းစနစ်"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"လတ်တလောများ"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"အကြောင်းကြားချက်"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"အမြန်ဆက်တင်များ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 6d143bd..f4fb8fa 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Alt er klart!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Sveip opp for å gå til startskjermen"</string>
     <string name="allset_description" msgid="6350320429953234580">"Du er klar til å begynne å bruke telefonen"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Du er klar til å begynne å bruke nettbrettet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Innstillinger for systemnavigasjon"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skjermdump"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tilbake"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-veksler"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nylige"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Varsler"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hurtiginnst."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index cd3383b..b6a71b9 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"सबै तयार भयो!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"होममा जान माथितिर स्वाइप गर्नुहोस्"</string>
     <string name="allset_description" msgid="6350320429953234580">"तपाईं आफ्नो फोन चलाउन थाल्न सक्नुहुन्छ"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"तपाईं अब आफ्नो ट्याब्लेट चलाउन थाल्न सक्नुहुन्छ"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"सिस्टम नेभिगेसनसम्बन्धी सेटिङ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"सेयर गर्नुहोस्"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रिनसट"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"पछाडि जानुहोस्"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME स्विचर"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"हालसालैका बटनहरू"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"सूचनाहरू"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"द्रुत सेटिङ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
 </resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 7772aae..4c02ea1 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Klaar"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swipe omhoog om naar het startscherm te gaan"</string>
     <string name="allset_description" msgid="6350320429953234580">"Je bent klaar om je telefoon te gebruiken"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Je bent klaar om je tablet te gebruiken"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Navigatie-instellingen van systeem"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Delen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 7740783..b76da38 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -22,7 +22,7 @@
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ପିନ୍‍"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
-    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ୍‍ ବ୍ୟବହାର ସେଟିଂସ୍‍"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ ବ୍ୟବହାର ସେଟିଂସ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍‌"</string>
     <string name="task_view_closed" msgid="9170038230110856166">"ଟାସ୍କ ବନ୍ଦ ହୋଇଯାଇଛି"</string>
@@ -38,7 +38,7 @@
     <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକୁ, ସିଧା ମୂଳ ସ୍କ୍ରିନରେ ସହଜରେ ଆକ୍ସେସ୍ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ପରିବର୍ତ୍ତିତ ହେବ। ତଳ ଧାଡ଼ିରେ ଥିବା ଆପଗୁଡ଼ିକ ଏକ ନୂଆ ଫୋଲ୍ଡରକୁ ମୁଭ୍ କରିଯିବ।"</string>
     <string name="hotseat_edu_accept" msgid="1611544083278999837">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
     <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ନାହିଁ, ଥାଉ"</string>
-    <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ସେଟିଂସ୍"</string>
+    <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ସେଟିଂସ"</string>
     <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯାଏ ଏବଂ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରିବର୍ତ୍ତିତ ହୋଇଥାଏ"</string>
     <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଇବାକୁ ଆପଗୁଡ଼ିକୁ ତଳ ଧାଡ଼ିରୁ ଟାଣି ଆଣନ୍ତୁ"</string>
     <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ଖାଲି ସ୍ଥାନରେ ଯୋଗ କରାଯାଇଛି"</string>
@@ -69,14 +69,15 @@
     <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ଆପଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ, ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ, ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ୍ କରନ୍ତୁ।"</string>
     <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ସବୁ ପ୍ରସ୍ତୁତ"</string>
     <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ହୋଇଗଲା"</string>
-    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ସେଟିଂସ୍"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ସେଟିଂସ"</string>
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ବଢ଼ିଆ!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"ଟ୍ୟୁଟୋରିଆଲ୍ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="allset_title" msgid="5021126669778966707">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବେ ପ୍ରସ୍ତୁତ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ମୂଳପୃଷ୍ଠାକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
     <string name="allset_description" msgid="6350320429953234580">"ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରିବା ପାଇଁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
-    <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ସେଟିଂସ୍"</annotation></string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
+    <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ ନାଭିଗେସନ ସେଟିଂସ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ସେୟାର୍ କରନ୍ତୁ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</string>
     <string name="action_split" msgid="2098009717623550676">"ସ୍ପ୍ଲିଟ୍"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ପଛକୁ ଫେରନ୍ତୁ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ସ୍ୱିଚର"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ବର୍ତ୍ତମାନର"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"କ୍ୱିକ ସେଟିଂସ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
 </resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 0173ee2..212492d 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਤਿਆਰ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="allset_description" msgid="6350320429953234580">"ਤੁਸੀਂ ਆਪਣਾ ਫ਼ੋਨ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ਤੁਸੀਂ ਆਪਣਾ ਟੈਬਲੈੱਟ ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਸੈਟਿੰਗਾਂ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ਸਾਂਝਾ ਕਰੋ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 19b666a..d8bbb43 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Wszystko gotowe"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Aby przejść na stronę główną, przesuń palcem w górę"</string>
     <string name="allset_description" msgid="6350320429953234580">"Teraz możesz zacząć używać telefonu"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Teraz możesz zacząć używać tabletu"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ustawienia nawigacji w systemie"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 4e7b7a6..e829210 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Deslize rapidamente para cima para aceder ao ecrã principal"</string>
     <string name="allset_description" msgid="6350320429953234580">"Já pode começar a utilizar o seu telemóvel"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Já pode começar a usar o seu tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Definições de navegação do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Fazer captura de ecrã"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index d4a56ac..30e59c2 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Deslize para cima para acessar a tela inicial"</string>
     <string name="allset_description" msgid="6350320429953234580">"Você já pode começar a usar seu smartphone"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Você já pode começar a usar seu tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configurações de navegação do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 38d596c..0294ada 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Gata!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Glisați în sus pentru a accesa ecranul de pornire"</string>
     <string name="allset_description" msgid="6350320429953234580">"Sunteți gata să folosiți telefonul"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Sunteți gata să folosiți tableta"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Setările de navigare ale sistemului"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Distribuiți"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captură de ecran"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 7d27436..8deb58e 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -76,6 +76,8 @@
     <string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Чтобы перейти на главный экран, проведите вверх."</string>
     <string name="allset_description" msgid="6350320429953234580">"Теперь вы можете использовать телефон."</string>
+    <!-- no translation found for allset_description_tablet (7332070270570039247) -->
+    <skip />
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системные настройки навигации"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Поделиться"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 990017e..360f189 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"සියල්ල සූදානම්!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"මුල් පිටුවට යාමට ඉහළට ස්වයිප් කරන්න"</string>
     <string name="allset_description" msgid="6350320429953234580">"ඔබ ඔබගේ දුරකථනය භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"ඔබ ඔබගේ ටැබ්ලටය භාවිත කිරීම පටන් ගැනීමට සූදානම්"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"පද්ධති සංචාලන සැකසීම්"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"බෙදා ගන්න"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"තිර රුව"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ආපසු"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME මාරුව"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"මෑත"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"දැනුම්දීම්"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ඉක්මන් සැකසීම්"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index a18f72c..5281c57 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Hotovo"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Potiahnutím nahor prejdete na plochu"</string>
     <string name="allset_description" msgid="6350320429953234580">"Telefón môžete začať používať"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Tablet môžete začať používať"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavenia navigácie systémom"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Zdieľať"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Späť"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Prepínač IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nedávne"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Upozornenia"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rýchle nastavenia"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 8203528..87ed18a 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Končano"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Povlecite navzgor za začetni zaslon"</string>
     <string name="allset_description" msgid="6350320429953234580">"Pripravljeni ste, da začnete uporabljati telefon"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Pripravljeni ste, da začnete uporabljati tablični računalnik."</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavitve krmarjenja po sistemu"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Posnetek zaslona"</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index f2b2fa8..828b440 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Plotësisht gati!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Rrëshqit shpejt lart për të shkuar tek \"Ekrani bazë\""</string>
     <string name="allset_description" msgid="6350320429953234580">"Je gati për të filluar përdorimin e telefonit tënd"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Je gati që të fillosh të përdorësh tabletin"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Cilësimet e navigimit të sistemit"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Ndaj"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Pamja e ekranit"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Pas"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Çelësi IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Të fundit"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Njoftimet"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cilësimet shpejt"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 309aaeb..8e5dcba 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Готово!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Превуците нагоре да бисте отворили почетни екран"</string>
     <string name="allset_description" msgid="6350320429953234580">"Спремни сте да почнете да користите телефон"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Спремни сте да почнете да користите таблет"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Подешавања кретања кроз систем"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Дели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 2f1b75a..bb9eaba 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Klart!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Svep uppåt för att öppna startskärmen"</string>
     <string name="allset_description" msgid="6350320429953234580">"Nu kan du börja använda telefonen"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Nu kan du börja använda surfplattan"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Systemnavigeringsinställningar"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Dela"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tillbaka"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-väljare"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Senaste"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Aviseringar"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Snabbinställn."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index e10d447..27b728b 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Tayari!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Telezesha kidole juu ili uende kwenye skrini ya kwanza"</string>
     <string name="allset_description" msgid="6350320429953234580">"Uko tayari kuanza kutumia simu yako"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Uko tayari kuanza kutumia kompyuta kibao yako"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mipangilio ya usogezaji kwenye mfumo"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Shiriki"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</string>
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
index 05e269b..e381cb0 100644
--- a/quickstep/res/values-sw720dp/dimens.xml
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -15,6 +15,11 @@
 */
 -->
 <resources>
-    <dimen name="overview_grid_row_spacing">44dp</dimen>
+    <dimen name="overview_task_margin">16dp</dimen>
+    <dimen name="overview_task_margin_grid">16dp</dimen>
+    <dimen name="overview_grid_side_margin">64dp</dimen>
+    <dimen name="overview_grid_row_spacing">36dp</dimen>
     <dimen name="overview_page_spacing">44dp</dimen>
+    <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+    <dimen name="task_thumbnail_icon_drawable_size_grid">44dp</dimen>
 </resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index ebf5eb2..0144430 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"அனைத்தையும் அமைத்துவிட்டீர்கள்!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"முகப்புத் திரைக்குச் செல்ல மேல்நோக்கி ஸ்வைப் செய்யுங்கள்"</string>
     <string name="allset_description" msgid="6350320429953234580">"மொபைலைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"உங்கள் டேப்லெட்டைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"சிஸ்டம் வழிசெலுத்தல் அமைப்புகள்"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"பகிர்"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ஸ்கிரீன்ஷாட்"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 8f43b3c..ebd24dc 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"అంతా సెట్ అయింది!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"మొదటి స్క్రీన్‌కు వెళ్లడానికి పైకి స్వైప్ చేయండి"</string>
     <string name="allset_description" msgid="6350320429953234580">"మీరు మీ ఫోన్‌ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"మీరు మీ టాబ్లెట్‌ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"సిస్టమ్ నావిగేషన్ సెట్టింగ్‌లు"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"షేర్ చేయండి"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్‌షాట్"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 62ab2d2..95b6c21 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"เรียบร้อยแล้ว"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ปัดขึ้นเพื่อไปที่หน้าแรก"</string>
     <string name="allset_description" msgid="6350320429953234580">"คุณเริ่มใช้โทรศัพท์ได้แล้ว"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"คุณเริ่มใช้แท็บเล็ตได้แล้ว"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"การตั้งค่าการนำทางของระบบ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"แชร์"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ภาพหน้าจอ"</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 3ecae96..53084e2 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Handa na ang lahat!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Mag-swipe pataas para pumunta sa Home"</string>
     <string name="allset_description" msgid="6350320429953234580">"Handa mo nang simulan ang paggamit sa iyong telepono"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Handa mo nang simulan ang paggamit sa iyong tablet"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mga setting ng navigation ng system"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Ibahagi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Bumalik"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME switcher"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Mga Kamakailan"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Mga Notification"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 19ced47..0e2cffb 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"İşlem tamam!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Ana ekrana gitmek için yukarı kaydırın"</string>
     <string name="allset_description" msgid="6350320429953234580">"Telefonunuzu kullanmaya hazırsınız"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Tabletinizi kullanmaya hazırsınız"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistem gezinme ayarları"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Paylaş"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekran görüntüsü"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Geri"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME değiştirici"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Son Kullanılanlar"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirimler"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hızlı Ayarlar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 231d128..5e4167d 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Готово."</string>
     <string name="allset_hint" msgid="2384632994739392447">"Щоб перейти на головний екран, проведіть пальцем угору"</string>
     <string name="allset_description" msgid="6350320429953234580">"Тепер ви можете користуватися телефоном"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Тепер ви можете користуватися планшетом"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системні налаштування навігації"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Поділитися"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Назад"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Перемикач IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Нещодавні"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Сповіщення"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Швидкі налаштув."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
 </resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 2b055ac..975a184 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"سب کچھ تیار ہے!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ہوم پر جانے کے لیے اوپر سوائپ کریں"</string>
     <string name="allset_description" msgid="6350320429953234580">"آپ اپنا فون استعمال شروع کرنے کے لیے تیار ہیں"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"آپ اپنے ٹیبلیٹ کا استعمال شروع کرنے کے لیے تیار ہیں"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"سسٹم نیویگیشن کی ترتیبات"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"اشتراک کریں"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"اسکرین شاٹ"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 3b9853f..f612987 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Hammasi tayyor!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Boshiga qaytish uchun tepaga suring"</string>
     <string name="allset_description" msgid="6350320429953234580">"Telefoningiz xizmatga tayyor"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Planshetingiz xizmatga tayyor"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tizim navigatsiya sozlamalari"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Ulashish"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skrinshot"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 1072c3a..253fca2 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Đã hoàn tất!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Vuốt lên để chuyển đến Màn hình chính"</string>
     <string name="allset_description" msgid="6350320429953234580">"Vậy là bạn đã sẵn sàng sử dụng điện thoại của mình"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Bạn đã sẵn sàng sử dụng máy tính bảng"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Chế độ cài đặt di chuyển trên hệ thống"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Chia sẻ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Chụp ảnh màn hình"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Quay lại"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Trình chuyển đổi IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Gần đây"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Thông báo"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cài đặt nhanh"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 4587d58..eaf6018 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"大功告成!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"向上滑动即可转到主屏幕"</string>
     <string name="allset_description" msgid="6350320429953234580">"您可以开始使用手机了"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"您可以开始使用平板电脑了"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"返回"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME 切换器"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"最近用过"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"快捷设置"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 2810771..123a016 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"設定完成!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"向上滑動即可前往主畫面"</string>
     <string name="allset_description" msgid="6350320429953234580">"您可以開始使用手機了"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"您可以開始使用平板電腦了"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統導覽設定"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"返回"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"輸入法編輯器切換工具"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"最近"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 847cb91..a2b204f 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"設定完成!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"向上滑動即可前往主畫面"</string>
     <string name="allset_description" msgid="6350320429953234580">"你可以開始使用手機了"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"你可以開始使用平板電腦了"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系統操作機制設定"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
@@ -102,10 +103,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"返回"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"輸入法編輯器切換器"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"最近使用"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 98ea6d7..41f6a7a 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -76,6 +76,7 @@
     <string name="allset_title" msgid="5021126669778966707">"Konke kusethiwe!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Swayiphela phezulu ukuze uye Ekhaya"</string>
     <string name="allset_description" msgid="6350320429953234580">"Usulungele ukuqala ukusebenzisa ifoni yakho"</string>
+    <string name="allset_description_tablet" msgid="7332070270570039247">"Usulungele ukuqala ukusebenzisa ithebulethi yakho"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Amasethingi wokuzulazula isistimu"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Yabelana"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</string>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 671a617..8f439a2 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
 
     <color name="chip_hint_foreground_color">#fff</color>
     <color name="chip_scrim_start_color">#39000000</color>
@@ -41,8 +41,6 @@
     <color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
     <!-- Must contrast gesture_tutorial_fake_wallpaper_color -->
     <color name="gesture_tutorial_fake_previous_task_view_color">#3C4043</color> <!-- Gray -->
-    <color name="gesture_tutorial_action_button_label_color">#FF000000</color>
-    <color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
     <color name="gesture_tutorial_taskbar_color">#202124</color>
 
     <!-- Mock hotseat -->
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3a4bb10..3b4a28b 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -23,8 +23,8 @@
     </string-array>
 
     <string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
-
     <string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string>
+    <string name="window_manager_proxy_class" translatable="false">com.android.quickstep.util.SystemWindowManagerProxy</string>
 
     <!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
          determines how many thumbnails will be fetched in the background. -->
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 926e10c..2b71768 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -110,8 +110,10 @@
     <dimen name="gestures_overscroll_finish_threshold">136dp</dimen>
 
     <!-- Tips Gesture Tutorial -->
-    <dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
-    <dimen name="gesture_tutorial_foldable_feedback_margin_start_end">140dp</dimen>
+    <dimen name="gesture_tutorial_feedback_margin_start_end">8dp</dimen>
+    <dimen name="gesture_tutorial_tablet_feedback_margin_start_end">140dp</dimen>
+    <dimen name="gesture_tutorial_feedback_margin_top">16dp</dimen>
+    <dimen name="gesture_tutorial_tablet_feedback_margin_top">24dp</dimen>
     <dimen name="gesture_tutorial_multi_row_task_view_spacing">72dp</dimen>
     <dimen name="gesture_tutorial_small_task_view_corner_radius">18dp</dimen>
     <dimen name="gesture_tutorial_mock_taskbar_height">80dp</dimen>
@@ -124,15 +126,46 @@
     <dimen name="gesture_tutorial_message_small_margin_bottom">4dp</dimen>
     <dimen name="gesture_tutorial_message_padding_start">26dp</dimen>
     <dimen name="gesture_tutorial_message_padding_end">18dp</dimen>
-    <dimen name="gesture_tutorial_foldable_message_padding_start_end">126dp</dimen>
+    <dimen name="gesture_tutorial_top_bar_margin_start">34dp</dimen>
+    <dimen name="gesture_tutorial_top_bar_margin_end">211dp</dimen>
+    <dimen name="gesture_tutorial_top_bar_button_margin_end">24dp</dimen>
+    <dimen name="gesture_tutorial_conversation_bottom_padding">66dp</dimen>
+    <integer name="gesture_tutorial_extra_messages_visibility">0</integer> <!-- VISIBLE -->
+    <dimen name="gesture_tutorial_message_margin_start">124dp</dimen>
+    <dimen name="gesture_tutorial_reply_margin_end">144dp</dimen>
+    <dimen name="gesture_tutorial_input_margin_start">34dp</dimen>
+    <dimen name="gesture_tutorial_input_margin_end">24dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_padding_start_end">126dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_1_margin">245dp</dimen>
+    <dimen name="gesture_tutorial_tablet_reply_1_margin">241dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_2_margin">401dp</dimen>
+    <dimen name="gesture_tutorial_tablet_message_3_margin">245dp</dimen>
+    <dimen name="gesture_tutorial_tablet_reply_2_margin">273dp</dimen>
 
     <!-- Gesture Tutorial mock conversation lists -->
     <dimen name="gesture_tutorial_conversation_icon_size">56dp</dimen>
     <dimen name="gesture_tutorial_conversation_icon_corner_radius">100dp</dimen>
     <dimen name="gesture_tutorial_conversation_list_padding_top">28dp</dimen>
     <dimen name="gesture_tutorial_conversation_line_padding_start">20dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_1_margin_end">217dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_2_margin_end">142dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_3_margin_end">190dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_4_margin_end">171dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_5_margin_end">198dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_6_margin_end">79dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_7_margin_end">174dp</dimen>
+    <dimen name="gesture_tutorial_conversation_line_8_margin_end">117dp</dimen>
+    <dimen name="gesture_tutorial_tablet_conversation_line_6_margin_end">65dp</dimen>
+    <dimen name="gesture_tutorial_tablet_conversation_line_8_margin_end">132dp</dimen>
+    <dimen name="gesture_tutorial_tablet_conversation_line_10_margin_end">161dp</dimen>
+    <integer name="gesture_tutorial_extra_conversations_visibility">0</integer> <!-- VISIBLE -->
+    <dimen name="gesture_tutorial_mock_button_margin_end">24dp</dimen>
+    <dimen name="gesture_tutorial_mock_button_margin_bottom">66dp</dimen>
 
     <!-- Gesture Tutorial mock hotseats -->
+    <dimen name="gesture_tutorial_hotseat_width">-1px</dimen> <!-- match_parent -->
+    <dimen name="gesture_tutorial_hotseat_height">-2px</dimen> <!-- wrap_content -->
+    <dimen name="gesture_tutorial_hotseat_padding_start_end">26dp</dimen>
     <dimen name="gesture_tutorial_hotseat_icon_size">60dp</dimen>
     <dimen name="gesture_tutorial_hotseat_icon_corner_radius">100dp</dimen>
     <dimen name="gesture_tutorial_hotseat_search_height">50dp</dimen>
@@ -148,11 +181,20 @@
     <dimen name="gesture_tutorial_webpage_small_corner_radius">4dp</dimen>
     <dimen name="gesture_tutorial_webpage_large_line_height">36dp</dimen>
     <dimen name="gesture_tutorial_webpage_small_line_height">22dp</dimen>
+    <dimen name="gesture_tutorial_webpage_url_margin_start_end">16dp</dimen>
+    <dimen name="gesture_tutorial_webpage_top_bar_button_margin_start">24dp</dimen>
+    <dimen name="gesture_tutorial_webpage_top_bar_margin_start">97dp</dimen>
+    <dimen name="gesture_tutorial_webpage_top_bar_margin_end">97dp</dimen>
+    <dimen name="gesture_tutorial_webpage_line_1_margin_end">126dp</dimen>
+    <dimen name="gesture_tutorial_webpage_line_2_margin_end">64dp</dimen>
+    <dimen name="gesture_tutorial_webpage_line_3_margin_end">151dp</dimen>
+    <dimen name="gesture_tutorial_webpage_block_margin_end">24dp</dimen>
+    <integer name="gesture_tutorial_webpage_extra_lines_visibility">0</integer> <!-- VISIBLE -->
 
     <!-- Gesture Tutorial mock taskbar -->
     <dimen name="gesture_tutorial_taskbar_icon_size">44dp</dimen>
     <dimen name="gesture_tutorial_taskbar_icon_corner_radius">100dp</dimen>
-    <dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
+    <dimen name="gesture_tutorial_taskbar_padding_start_end">52dp</dimen>
 
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">40dp</dimen>
@@ -217,6 +259,7 @@
     <dimen name="taskbar_contextual_buttons_size">35dp</dimen>
     <dimen name="taskbar_stashed_size">24dp</dimen>
     <dimen name="taskbar_stashed_handle_width">220dp</dimen>
+    <dimen name="taskbar_unstash_input_area">316dp</dimen>
     <dimen name="taskbar_stashed_handle_height">4dp</dimen>
     <dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
     <dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 3ee2af0..f80deeb 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -182,8 +182,10 @@
     <string name="allset_title">All set!</string>
     <!-- Hint string at the bottom of "All Set" page [CHAR LIMIT=NONE] -->
     <string name="allset_hint">Swipe up to go Home</string>
-    <!-- Description of "All Set" page [CHAR LIMIT=NONE] -->
+    <!-- Description of "All Set" page on phones [CHAR LIMIT=NONE] -->
     <string name="allset_description">You\u2019re ready to start using your phone</string>
+    <!-- Description of "All Set" page on tablets [CHAR LIMIT=NONE] -->
+    <string name="allset_description_tablet">You\u2019re ready to start using your tablet</string>
     <!-- String linking to navigation settings on "All Set" page [CHAR LIMIT=NONE] -->
     <string name="allset_navigation_settings"><annotation id="link">System navigation settings</annotation></string>
 
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 2efe72e..7225220 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -47,6 +47,12 @@
         <item name="android:lineHeight">44sp</item>
     </style>
 
+    <style name="TextAppearance.GestureTutorial.Feedback.Title.AllSet"
+        parent="TextAppearance.GestureTutorial.Feedback.Title">
+        <item name="android:letterSpacing">0.03</item>
+        <item name="android:lineHeight">44sp</item>
+    </style>
+
     <style name="TextAppearance.GestureTutorial.Dialog.Title"
         parent="TextAppearance.GestureTutorial.Feedback.Title">
         <item name="android:gravity">center_horizontal</item>
@@ -61,6 +67,12 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:fontFamily">google-sans-text</item>
         <item name="android:letterSpacing">0.03</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:lineHeight">20sp</item>
+    </style>
+
+    <style name="TextAppearance.GestureTutorial.Feedback.Subtitle.AllSet"
+        parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
         <item name="android:textSize">18sp</item>
         <item name="android:lineHeight">24sp</item>
     </style>
@@ -77,8 +89,8 @@
     <style name="TextAppearance.GestureTutorial.Feedback.Subtext"
         parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
         <item name="android:textSize">16sp</item>
-        <item name="android:textColor">@color/gesture_tutorial_primary_color</item>
-        <item name="android:gravity">center</item>
+        <item name="android:textColor">?android:attr/colorAccent</item>
+        <item name="android:gravity">start</item>
     </style>
 
     <style name="TextAppearance.GestureTutorial.Feedback.Subtext.Dark"
@@ -89,7 +101,7 @@
     <style name="TextAppearance.GestureTutorial.ButtonLabel"
         parent="TextAppearance.GestureTutorial.CallToAction">
         <item name="android:gravity">center</item>
-        <item name="android:textColor">@color/gesture_tutorial_action_button_label_color</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="android:letterSpacing">0.02</item>
         <item name="android:textSize">16sp</item>
         <item name="android:textAllCaps">false</item>
@@ -97,12 +109,12 @@
 
     <style name="TextAppearance.GestureTutorial.CancelButtonLabel"
         parent="TextAppearance.GestureTutorial.ButtonLabel">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 
     <style name="TextAppearance.GestureTutorial.TextButtonLabel"
         parent="TextAppearance.GestureTutorial.ButtonLabel">
-        <item name="android:textColor">@color/gesture_tutorial_primary_color</item>
+        <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 
     <style name="TextAppearance.GestureTutorial.LinkText"
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index a26f3e8..6f0f993 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -25,9 +25,10 @@
 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.DisplayController.CHANGE_NAVIGATION_MODE;
+import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
 import android.animation.AnimatorSet;
@@ -36,14 +37,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.graphics.Insets;
 import android.hardware.SensorManager;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.view.Display;
 import android.view.View;
-import android.view.WindowInsets;
 import android.window.SplashScreen;
 
 import androidx.annotation.Nullable;
@@ -63,6 +63,7 @@
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.RunnableList;
@@ -70,9 +71,6 @@
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.TouchInteractionService.TISBinder;
@@ -100,8 +98,7 @@
 /**
  * Extension of Launcher activity to provide quickstep specific functionality
  */
-public abstract class BaseQuickstepLauncher extends Launcher
-        implements NavigationModeChangeListener {
+public abstract class BaseQuickstepLauncher extends Launcher {
 
     private DepthController mDepthController = new DepthController(this);
     private QuickstepTransitionManager mAppTransitionManager;
@@ -129,7 +126,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
         addMultiWindowModeChangedListener(mDepthController);
         initUnfoldTransitionProgressProvider();
     }
@@ -159,8 +155,6 @@
             mUnfoldTransitionProgressProvider.destroy();
         }
 
-        SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
-
         mTISBindHelper.onDestroy();
         if (mTaskbarManager != null) {
             mTaskbarManager.clearActivity(this);
@@ -187,14 +181,6 @@
     }
 
     @Override
-    public void onNavigationModeChanged(Mode newMode) {
-        getDragLayer().recreateControllers();
-        if (mActionsView != null) {
-            mActionsView.updateVerticalMargin(newMode);
-        }
-    }
-
-    @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
@@ -303,15 +289,14 @@
     protected void setupViews() {
         super.setupViews();
 
-        SysUINavigationMode.INSTANCE.get(this).updateMode();
         mActionsView = findViewById(R.id.overview_actions_view);
         RecentsView overviewPanel = (RecentsView) getOverviewPanel();
         SplitSelectStateController controller =
-                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this),
-                        getStateManager(), getDepthController());
+                new SplitSelectStateController(this, mHandler, getStateManager(),
+                        getDepthController());
         overviewPanel.init(mActionsView, controller);
         mActionsView.setDp(getDeviceProfile());
-        mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
+        mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
 
         mAppTransitionManager = new QuickstepTransitionManager(this);
         mAppTransitionManager.registerRemoteAnimations();
@@ -343,6 +328,7 @@
                             getSystemService(SensorManager.class),
                             getMainThreadHandler(),
                             getMainExecutor(),
+                            /* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
                             /* tracingTagPrefix= */ "launcher"
                     );
 
@@ -431,7 +417,7 @@
 
     @Override
     public float[] getNormalOverviewScaleAndOffset() {
-        return SysUINavigationMode.getMode(this).hasGestures
+        return DisplayController.getNavigationMode(this).hasGestures
                 ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET};
     }
 
@@ -461,7 +447,7 @@
     }
 
     public boolean shouldBackButtonBeHidden(LauncherState toState) {
-        Mode mode = SysUINavigationMode.getMode(this);
+        NavigationMode mode = DisplayController.getNavigationMode(this);
         boolean shouldBackButtonBeHidden = mode.hasGestures
                 && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
                 && hasWindowFocus()
@@ -479,7 +465,7 @@
      */
     private void onLauncherStateOrFocusChanged() {
         boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
-        if (SysUINavigationMode.getMode(this) == TWO_BUTTONS) {
+        if (DisplayController.getNavigationMode(this) == TWO_BUTTONS) {
             UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
                     shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
         }
@@ -548,11 +534,26 @@
     }
 
     /**
-     * Adds a new launch cookie for the activity launch of the given {@param info} if supported.
+     * Adds a new launch cookie for the activity launch if supported.
+     *
+     * @param info the item info for the launch
+     * @param opts the options to set the launchCookie on.
      */
     public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
+        IBinder launchCookie = getLaunchCookie(info);
+        if (launchCookie != null) {
+            opts.setLaunchCookie(launchCookie);
+        }
+    }
+
+    /**
+     * Return a new launch cookie for the activity launch if supported.
+     *
+     * @param info the item info for the launch
+     */
+    public IBinder getLaunchCookie(ItemInfo info) {
         if (info == null) {
-            return;
+            return null;
         }
         switch (info.container) {
             case LauncherSettings.Favorites.CONTAINER_DESKTOP:
@@ -566,8 +567,7 @@
                     break;
                 }
                 // Reset any existing launch cookies associated with the cookie
-                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
-                return;
+                return ObjectWrapper.wrap(NO_MATCHING_ID);
         }
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -578,10 +578,9 @@
                 break;
             default:
                 // Reset any existing launch cookies associated with the cookie
-                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
-                return;
+                return ObjectWrapper.wrap(NO_MATCHING_ID);
         }
-        opts.setLaunchCookie(ObjectWrapper.wrap(new Integer(info.id)));
+        return ObjectWrapper.wrap(new Integer(info.id));
     }
 
     public void setHintUserWillBeActive() {
@@ -596,6 +595,13 @@
         if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
             getStateManager().moveToRestState();
         }
+
+        if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+            getDragLayer().recreateControllers();
+            if (mActionsView != null) {
+                mActionsView.updateVerticalMargin(info.navigationMode);
+            }
+        }
     }
 
     @Override
@@ -605,17 +611,4 @@
             mDepthController.dump(prefix, writer);
         }
     }
-
-    @Override
-    public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
-            WindowInsets oldInsets) {
-        // Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
-        // would count towards it). This is used for the bottom protection in All Apps for example.
-        if (SysUINavigationMode.getMode(this) == NO_BUTTON) {
-            Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
-            Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
-                    oldTappableInsets.right, 0);
-            updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
-        }
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 661053a..62603e9 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -16,9 +16,9 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
 
 import android.animation.Animator;
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 3eb1935e..0e391ce 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -28,6 +28,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.mapBoundToRange;
 import static com.android.launcher3.Utilities.postAsyncCallback;
@@ -44,7 +45,8 @@
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
@@ -77,6 +79,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
 import android.view.SurfaceControl;
@@ -109,6 +112,7 @@
 import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.views.ScrimView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.quickstep.LauncherBackAnimationController;
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskViewUtils;
@@ -135,6 +139,7 @@
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
 
@@ -213,6 +218,7 @@
     private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
     private RemoteTransitionCompat mLauncherOpenTransition;
 
+    private LauncherBackAnimationController mBackAnimationController;
     private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationStart(Animator animation) {
@@ -238,6 +244,8 @@
         mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
         mHandler = new Handler(Looper.getMainLooper());
         mDeviceProfile = mLauncher.getDeviceProfile();
+        mBackAnimationController = new LauncherBackAnimationController(
+                mDeviceProfile, mLauncher, this);
 
         Resources res = mLauncher.getResources();
         mContentScale = res.getFloat(R.dimen.content_scale);
@@ -579,8 +587,8 @@
                 }
             }
 
-            // Pause page indicator animations as they lead to layer trashing.
-            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
+            // Pause expensive view updates as they can lead to layer thrashing and skipped frames.
+            mLauncher.pauseExpensiveViewUpdates();
 
             endListener = () -> {
                 viewsToAnimate.forEach(view -> {
@@ -590,7 +598,7 @@
                 if (scrimEnabled) {
                     mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT);
                 }
-                mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
+                mLauncher.resumeExpensiveViewUpdates();
             };
         }
 
@@ -611,9 +619,28 @@
         RecentsView overview = mLauncher.getOverviewPanel();
         ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
                 RecentsView.CONTENT_ALPHA, alphas);
+        Log.d(BAD_STATE, "QTM composeViewContentAnimator alphas=" + Arrays.toString(alphas));
+        alpha.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                Log.d(BAD_STATE, "QTM composeViewContentAnimator onStart");
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                float alpha = overview == null ? -1 : RecentsView.CONTENT_ALPHA.get(overview);
+                Log.d(BAD_STATE, "QTM composeViewContentAnimator onCancel, alpha=" + alpha);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                Log.d(BAD_STATE, "QTM composeViewContentAnimator onEnd");
+            }
+        });
         alpha.setDuration(CONTENT_ALPHA_DURATION);
         alpha.setInterpolator(LINEAR);
         anim.play(alpha);
+        Log.d(BAD_STATE, "QTM composeViewContentAnimator setFreezeVisibility=true");
         overview.setFreezeViewVisibility(true);
 
         ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(overview, SCALE_PROPERTY, scales);
@@ -622,6 +649,7 @@
         anim.play(scaleAnim);
 
         return () -> {
+            Log.d(BAD_STATE, "QTM composeViewContentAnimator onEnd setFreezeVisibility=false");
             overview.setFreezeViewVisibility(false);
             SCALE_PROPERTY.set(overview, 1f);
             mLauncher.getStateManager().reapplyState();
@@ -1136,6 +1164,9 @@
             mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
             SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
         }
+        if (mBackAnimationController != null) {
+            mBackAnimationController.registerBackCallbacks(mHandler);
+        }
     }
 
     public void onActivityDestroyed() {
@@ -1171,6 +1202,10 @@
             mLauncherOpenTransition = null;
             mWallpaperOpenTransitionRunner = null;
         }
+        if (mBackAnimationController != null) {
+            mBackAnimationController.unregisterBackCallbacks();
+            mBackAnimationController = null;
+        }
     }
 
     private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
@@ -1186,6 +1221,19 @@
         return false;
     }
 
+    private boolean hasMultipleTargetsWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+        int numTargets = 0;
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == mode) {
+                numTargets++;
+            }
+            if (numTargets > 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @return Runner that plays when user goes to Launcher
      * ie. pressing home, swiping up from nav bar.
@@ -1288,8 +1336,8 @@
             }
         }
 
-        return mLauncher.getFirstMatchForAppClose(launchCookieItemId,
-                packageName, UserHandle.of(runningTaskTarget.taskInfo.userId));
+        return mLauncher.getFirstMatchForAppClose(launchCookieItemId, packageName,
+                UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */);
     }
 
     private @NonNull RectF getDefaultWindowTargetRect() {
@@ -1310,8 +1358,9 @@
     /**
      * Closing animator that animates the window into its final location on the workspace.
      */
-    private void getClosingWindowAnimators(AnimatorSet animation,
-            RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) {
+    private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
+            RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
+            RectF closingWindowStartRect) {
         FloatingIconView floatingIconView = null;
         FloatingWidgetView floatingWidget = null;
         RectF targetRect = new RectF();
@@ -1343,8 +1392,7 @@
             targetRect.set(getDefaultWindowTargetRect());
         }
 
-        final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
-        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher,
+        RectFSpringAnim anim = new RectFSpringAnim(closingWindowStartRect, targetRect, mLauncher,
                 mDeviceProfile);
 
         // Hook up floating views to the closing window animators.
@@ -1378,7 +1426,7 @@
 
             final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
             FloatingWidgetView finalFloatingWidget = floatingWidget;
-            RectFSpringAnim.OnUpdateListener  runner = new SpringAnimRunner(targets, targetRect,
+            RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
                     windowTargetBounds) {
                 @Override
                 public void onUpdate(RectF currentRectF, float progress) {
@@ -1402,6 +1450,7 @@
                 anim.start(mLauncher, velocityPxPerS);
             }
         });
+        return anim;
     }
 
     /**
@@ -1526,6 +1575,103 @@
     }
 
     /**
+     * Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate
+     * the transition.
+     */
+    public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            boolean fromUnlock,
+            RectF startRect) {
+        AnimatorSet anim = null;
+        RectFSpringAnim rectFSpringAnim = null;
+
+        RemoteAnimationProvider provider = mRemoteAnimationProvider;
+        if (provider != null) {
+            anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
+        }
+
+        if (anim == null) {
+            anim = new AnimatorSet();
+
+            final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
+                    || launcherIsATargetWithMode(appTargets, MODE_OPENING);
+
+            View launcherView = findLauncherView(appTargets);
+            boolean playFallBackAnimation = (launcherView == null
+                    && launcherIsForceInvisibleOrOpening)
+                    || mLauncher.getWorkspace().isOverlayShown()
+                    || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
+
+            boolean playWorkspaceReveal = true;
+            boolean skipAllAppsScale = false;
+            if (fromUnlock) {
+                anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
+            } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
+                    && !playFallBackAnimation) {
+                // Use a fixed velocity to start the animation.
+                float velocityPxPerS = DynamicResource.provider(mLauncher)
+                        .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
+                PointF velocity = new PointF(0, -velocityPxPerS);
+                rectFSpringAnim = getClosingWindowAnimators(
+                        anim, appTargets, launcherView, velocity, startRect);
+                if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+                    anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
+                            true /* animateOverviewScrim */, launcherView).getAnimators());
+
+                    if (!areAllTargetsTranslucent(appTargets)) {
+                        anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+                                BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher)));
+                    }
+
+                    // We play StaggeredWorkspaceAnim as a part of the closing window animation.
+                    playWorkspaceReveal = false;
+                } else {
+                    // Skip scaling all apps, otherwise FloatingIconView will get wrong
+                    // layout bounds.
+                    skipAllAppsScale = true;
+                }
+            } else {
+                anim.play(getFallbackClosingWindowAnimators(appTargets));
+            }
+
+            // Normally, we run the launcher content animation when we are transitioning
+            // home, but if home is already visible, then we don't want to animate the
+            // contents of launcher unless we know that we are animating home as a result
+            // of the home button press with quickstep, which will result in launcher being
+            // started on touch down, prior to the animation home (and won't be in the
+            // targets list because it is already visible). In that case, we force
+            // invisibility on touch down, and only reset it after the animation to home
+            // is initialized.
+            if (launcherIsForceInvisibleOrOpening) {
+                addCujInstrumentation(
+                        anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+                // Only register the content animation for cancellation when state changes
+                mLauncher.getStateManager().setCurrentAnimation(anim);
+
+                if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+                    Pair<AnimatorSet, Runnable> contentAnimator =
+                            getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
+                                    skipAllAppsScale);
+                    anim.play(contentAnimator.first);
+                    anim.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            contentAnimator.second.run();
+                        }
+                    });
+                } else {
+                    if (playWorkspaceReveal) {
+                        anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+                    }
+                }
+            }
+        }
+
+        return new Pair(rectFSpringAnim, anim);
+    }
+
+    /**
      * Remote animation runner for animation from the app to Launcher, including recents.
      */
     protected class WallpaperOpenLauncherAnimationRunner implements RemoteAnimationFactory {
@@ -1565,83 +1711,12 @@
                 mLauncher.getStateManager().moveToRestState();
             }
 
-            AnimatorSet anim = null;
-            RemoteAnimationProvider provider = mRemoteAnimationProvider;
-            if (provider != null) {
-                anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
-            }
-
-            if (anim == null) {
-                anim = new AnimatorSet();
-
-                final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
-                        || launcherIsATargetWithMode(appTargets, MODE_OPENING);
-
-                View launcherView = findLauncherView(appTargets);
-                boolean playFallBackAnimation = (launcherView == null
-                        && launcherIsForceInvisibleOrOpening)
-                        || mLauncher.getWorkspace().isOverlayShown();
-
-                boolean playWorkspaceReveal = true;
-                boolean skipAllAppsScale = false;
-                if (mFromUnlock) {
-                    anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
-                } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
-                        && !playFallBackAnimation) {
-                    // Use a fixed velocity to start the animation.
-                    float velocityPxPerS = DynamicResource.provider(mLauncher)
-                            .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
-                    PointF velocity = new PointF(0, -velocityPxPerS);
-                    getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
-                    if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
-                        anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
-                                true /* animateOverviewScrim */, launcherView).getAnimators());
-                        // We play StaggeredWorkspaceAnim as a part of the closing window animation.
-                        playWorkspaceReveal = false;
-                    } else {
-                        // Skip scaling all apps, otherwise FloatingIconView will get wrong
-                        // layout bounds.
-                        skipAllAppsScale = true;
-                    }
-                } else {
-                    anim.play(getFallbackClosingWindowAnimators(appTargets));
-                }
-
-                // Normally, we run the launcher content animation when we are transitioning
-                // home, but if home is already visible, then we don't want to animate the
-                // contents of launcher unless we know that we are animating home as a result
-                // of the home button press with quickstep, which will result in launcher being
-                // started on touch down, prior to the animation home (and won't be in the
-                // targets list because it is already visible). In that case, we force
-                // invisibility on touch down, and only reset it after the animation to home
-                // is initialized.
-                if (launcherIsForceInvisibleOrOpening) {
-                    addCujInstrumentation(
-                            anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
-                    // Only register the content animation for cancellation when state changes
-                    mLauncher.getStateManager().setCurrentAnimation(anim);
-
-                    if (mLauncher.isInState(LauncherState.ALL_APPS)) {
-                        Pair<AnimatorSet, Runnable> contentAnimator =
-                                getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
-                                        skipAllAppsScale);
-                        anim.play(contentAnimator.first);
-                        anim.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                contentAnimator.second.run();
-                            }
-                        });
-                    } else {
-                        if (playWorkspaceReveal) {
-                            anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
-                        }
-                    }
-                }
-            }
+            Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
+                    appTargets, wallpaperTargets, mFromUnlock,
+                    new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx));
 
             mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
-            result.setAnimation(anim, mLauncher);
+            result.setAnimation(pair.second, mLauncher);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index d37e530..0284ae4 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -194,7 +194,7 @@
     @Override
     protected void onDraw(Canvas canvas) {
         if (mDividerType == DividerType.LINE) {
-            int l = (getWidth() - getPaddingLeft() - mDividerSize[0]) / 2;
+            int l = (getWidth() - mDividerSize[0]) / 2;
             int t = getHeight() - (getPaddingBottom() / 2);
             int radius = mDividerSize[1];
             canvas.drawRoundRect(l, t, l + mDividerSize[0], t + mDividerSize[1], radius, radius,
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 8424564..0e534f4 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -15,20 +15,6 @@
  */
 package com.android.launcher3.model;
 
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB_ACCESSIBILITY;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB_ACCESSIBILITY;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.DISABLED_BY_ADMIN_MESSAGE;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_PERSONAL_TAB;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_WORK_TAB;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_FOLDER_NAME;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU_ACCEPT;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_ENABLE_BUTTON;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_DESCRIPTION;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_TITLE;
-import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSE_BUTTON;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.formatElapsedTime;
 
@@ -46,7 +32,6 @@
 import static java.util.stream.Collectors.toCollection;
 
 import android.app.StatsManager;
-import android.app.admin.DevicePolicyManager;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionManager;
 import android.app.prediction.AppPredictor;
@@ -151,42 +136,6 @@
     }
 
     @Override
-    @WorkerThread
-    public void loadStringCache(StringCache cache) {
-        cache.loadDefaultStrings(mContext);
-
-        cache.workProfileEdu = getEnterpriseString(WORK_PROFILE_EDU, cache.workProfileEdu);
-        cache.workProfileEduAccept = getEnterpriseString(
-                WORK_PROFILE_EDU_ACCEPT, cache.workProfileEduAccept);
-        cache.workProfilePausedTitle = getEnterpriseString(
-                WORK_PROFILE_PAUSED_TITLE, cache.workProfilePausedTitle);
-        cache.workProfilePausedDescription = getEnterpriseString(
-                WORK_PROFILE_PAUSED_DESCRIPTION, cache.workProfilePausedDescription);
-        cache.workProfilePauseButton = getEnterpriseString(
-                WORK_PROFILE_PAUSE_BUTTON, cache.workProfilePauseButton);
-        cache.workProfileEnableButton = getEnterpriseString(
-                WORK_PROFILE_ENABLE_BUTTON, cache.workProfileEnableButton);
-        cache.allAppsWorkTab = getEnterpriseString(ALL_APPS_WORK_TAB, cache.allAppsWorkTab);
-        cache.allAppsPersonalTab = getEnterpriseString(
-                ALL_APPS_PERSONAL_TAB, cache.allAppsPersonalTab);
-        cache.allAppsWorkTabAccessibility = getEnterpriseString(
-                ALL_APPS_WORK_TAB_ACCESSIBILITY, cache.allAppsWorkTabAccessibility);
-        cache.allAppsPersonalTabAccessibility = getEnterpriseString(
-                ALL_APPS_PERSONAL_TAB_ACCESSIBILITY, cache.allAppsPersonalTabAccessibility);
-        cache.workFolderName = getEnterpriseString(WORK_FOLDER_NAME, cache.workFolderName);
-        cache.widgetsWorkTab = getEnterpriseString(WIDGETS_WORK_TAB, cache.widgetsWorkTab);
-        cache.widgetsPersonalTab = getEnterpriseString(
-                WIDGETS_PERSONAL_TAB, cache.widgetsPersonalTab);
-        cache.disabledByAdminMessage = getEnterpriseString(
-                DISABLED_BY_ADMIN_MESSAGE, cache.disabledByAdminMessage);
-    }
-
-    private String getEnterpriseString(String updatableStringId, String defaultString) {
-        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
-        return dpm.getString(updatableStringId, () -> defaultString);
-    }
-
-    @Override
     public void workspaceLoadComplete() {
         super.workspaceLoadComplete();
         recreatePredictors();
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
index 1f268cc..07d3a51 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
@@ -17,17 +17,17 @@
 package com.android.launcher3.statehandlers;
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
 import static com.android.quickstep.AnimatedFloat.VALUE;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SystemUiProxy;
 
 /**
@@ -48,7 +48,7 @@
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation animation) {
-        if (SysUINavigationMode.getMode(mLauncher) != TWO_BUTTONS) {
+        if (DisplayController.getNavigationMode(mLauncher) != TWO_BUTTONS) {
             return;
         }
 
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index d54b9e7..3b02599 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -23,6 +23,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.app.WallpaperManager;
 import android.os.IBinder;
 import android.os.SystemProperties;
 import android.util.FloatProperty;
@@ -42,7 +43,6 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.systemui.shared.system.BlurUtils;
-import com.android.systemui.shared.system.WallpaperManagerCompat;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -127,7 +127,7 @@
      */
     private int mMaxBlurRadius;
     private boolean mCrossWindowBlursEnabled;
-    private WallpaperManagerCompat mWallpaperManager;
+    private WallpaperManager mWallpaperManager;
     private SurfaceControl mSurface;
     /**
      * How visible the -1 overlay is, from 0 to 1.
@@ -168,7 +168,7 @@
     private void ensureDependencies() {
         if (mWallpaperManager == null) {
             mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
-            mWallpaperManager = new WallpaperManagerCompat(mLauncher);
+            mWallpaperManager = mLauncher.getSystemService(WallpaperManager.class);
         }
 
         if (mLauncher.getRootView() != null && mOnAttachListener == null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index 17635df..4e1f54c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -23,14 +23,14 @@
 import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.AppLauncher;
 
 import java.util.ArrayList;
 import java.util.List;
 
 // TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar.
 /** Base for common behavior between taskbar window contexts. */
-public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext,
+public abstract class BaseTaskbarContext extends ContextThemeWrapper implements AppLauncher,
         DeviceProfileListenable {
 
     protected final LayoutInflater mLayoutInflater;
@@ -64,6 +64,9 @@
     /** Callback invoked when a drag is initiated within this context. */
     public abstract void onDragStart();
 
+    /** Callback invoked when a drag is finished within this context. */
+    public abstract void onDragEnd();
+
     /** Callback invoked when a popup is shown or closed within this context. */
     public abstract void onPopupVisibilityChanged(boolean isVisible);
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index af53ae2..793d987 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -27,6 +27,7 @@
 import android.view.WindowManagerGlobal;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
@@ -34,7 +35,6 @@
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfo;
@@ -119,6 +119,24 @@
     }
 
     /**
+     * Enables manual taskbar stashing. This method should only be used for tests that need to
+     * stash/unstash the taskbar.
+     */
+    @VisibleForTesting
+    public void enableManualStashingForTests(boolean enableManualStashing) {
+        mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
+    }
+
+    /**
+     * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
+     * taskbar at the end of a test.
+     */
+    @VisibleForTesting
+    public void unstashTaskbarIfStashed() {
+        mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
+    }
+
+    /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
     public void onLauncherResumedOrPaused(boolean isResumed) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8f0b934..0f3a6ee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -21,6 +21,8 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.ResourceUtils.getBoolByName;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -47,6 +49,7 @@
 import android.view.RoundedCorner;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
@@ -68,14 +71,16 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -91,6 +96,8 @@
  */
 public class TaskbarActivityContext extends BaseTaskbarContext {
 
+    private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar";
+
     private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
             SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
     private static final String TAG = "TaskbarActivityContext";
@@ -108,12 +115,13 @@
     // The size we should return to when we call setTaskbarWindowFullscreen(false)
     private int mLastRequestedNonFullscreenHeight;
 
-    private final SysUINavigationMode.Mode mNavMode;
+    private final NavigationMode mNavMode;
     private final boolean mImeDrawsImeNavBar;
     private final ViewCache mViewCache = new ViewCache();
 
     private final boolean mIsSafeModeEnabled;
     private final boolean mIsUserSetupComplete;
+    private final boolean mIsNavBarForceVisible;
     private final boolean mIsNavBarKidsMode;
     private boolean mIsDestroyed = false;
     // The flag to know if the window is excluded from magnification region computation.
@@ -128,18 +136,20 @@
         super(windowContext);
         mDeviceProfile = dp;
 
-        mNavMode = SysUINavigationMode.getMode(windowContext);
-        mImeDrawsImeNavBar = SysUINavigationMode.getImeDrawsImeNavBar(windowContext);
+        final Resources resources = getResources();
+
+        mNavMode = DisplayController.getNavigationMode(windowContext);
+        mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
                 () -> getPackageManager().isSafeMode());
         mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
+        mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(
+                Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_FORCE_VISIBLE), 0);
         mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
                 Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
 
-        final Resources resources = getResources();
         updateIconSize(resources);
-
         mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size);
 
         // Inflate views.
@@ -178,8 +188,8 @@
                 new TaskbarDragLayerController(this, mDragLayer),
                 new TaskbarViewController(this, taskbarView),
                 new TaskbarScrimViewController(this, taskbarScrimView),
-                new TaskbarUnfoldAnimationController(unfoldTransitionProgressProvider,
-                        mWindowManager),
+                new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
+                        mWindowManager, WindowManagerGlobal.getWindowManagerService()),
                 new TaskbarKeyguardController(this),
                 new StashedHandleViewController(this, stashedHandleView),
                 new TaskbarStashController(this),
@@ -254,11 +264,11 @@
     }
 
     public boolean isThreeButtonNav() {
-        return mNavMode == Mode.THREE_BUTTONS;
+        return mNavMode == NavigationMode.THREE_BUTTONS;
     }
 
     public boolean isGestureNav() {
-        return mNavMode == Mode.NO_BUTTON;
+        return mNavMode == NavigationMode.NO_BUTTON;
     }
 
     public boolean imeDrawsImeNavBar() {
@@ -383,6 +393,11 @@
     }
 
     @Override
+    public void onDragEnd() {
+        maybeSetTaskbarWindowNotFullscreen();
+    }
+
+    @Override
     public void onPopupVisibilityChanged(boolean isVisible) {
         setTaskbarWindowFocusable(isVisible);
     }
@@ -427,7 +442,8 @@
         mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
                 mControllers.navbarButtonsViewController.isHomeDisabled());
         mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
-        mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
+        mControllers.taskbarStashController.updateStateForSysuiFlags(
+                systemUiStateFlags, fromInit || !isUserSetupComplete());
         mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
                 fromInit);
         mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
@@ -485,6 +501,17 @@
         setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
     }
 
+    /**
+     * Reverts Taskbar window to its original size, if all floating views are closed and there is
+     * no system drag operation in progress.
+     */
+    void maybeSetTaskbarWindowNotFullscreen() {
+        if (AbstractFloatingView.getAnyView(this, TYPE_ALL) == null
+                && !mControllers.taskbarDragController.isSystemDragInProgress()) {
+            setTaskbarWindowFullscreen(false);
+        }
+    }
+
     public boolean isTaskbarWindowFullscreen() {
         return mIsFullscreen;
     }
@@ -622,12 +649,16 @@
                         Toast.makeText(this, R.string.safemode_shortcut_error,
                                 Toast.LENGTH_SHORT).show();
                     } else  if (info.isPromise()) {
+                        TestLogging.recordEvent(
+                                TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
                         intent = new PackageManagerHelper(this)
                                 .getMarketIntent(info.getTargetPackage())
                                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                         startActivity(intent);
 
                     } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                        TestLogging.recordEvent(
+                                TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
                         String id = info.getDeepShortcutId();
                         String packageName = intent.getPackage();
                         getSystemService(LauncherApps.class)
@@ -656,6 +687,7 @@
         Intent intent = new Intent(info.getIntent())
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
+            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
             if (info.user.equals(Process.myUserHandle())) {
                 // TODO(b/216683257): Use startActivityForResult for search results that require it.
                 startActivity(intent);
@@ -694,6 +726,10 @@
         return mIsNavBarKidsMode && isThreeButtonNav();
     }
 
+    protected boolean isNavBarForceVisible() {
+        return mIsNavBarForceVisible;
+    }
+
     /**
      * Called when we determine the touchable region.
      *
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index d2e24e5..a5999cc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -116,7 +116,7 @@
         taskbarEduController.init(this);
         taskbarPopupController.init(this);
         taskbarForceVisibleImmersiveController.init(this);
-        taskbarAllAppsController.init(this);
+        taskbarAllAppsController.init(this, sharedState);
 
         mControllersToLog = new LoggableTaskbarController[] {
                 taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -152,6 +152,7 @@
         taskbarAutohideSuspendController.onDestroy();
         taskbarPopupController.onDestroy();
         taskbarForceVisibleImmersiveController.onDestroy();
+        taskbarAllAppsController.onDestroy();
 
         mControllersToLog = null;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index c9f7f7e..e2ab443 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -55,22 +56,22 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
-import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ClipDescriptionCompat;
-import com.android.systemui.shared.system.LauncherAppsCompat;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Collections;
 
 /**
  * Handles long click on Taskbar items to start a system drag and drop operation.
@@ -129,7 +130,7 @@
         if (!(view instanceof BubbleTextView)) {
             return false;
         }
-
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
         BubbleTextView btv = (BubbleTextView) view;
         mActivity.onDragStart();
         btv.post(() -> {
@@ -165,24 +166,7 @@
         dragLayerY += dragRect.top;
 
         DragOptions dragOptions = new DragOptions();
-        dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
-            private DragView mDragView;
-
-            @Override
-            public boolean shouldStartDrag(double distanceDragged) {
-                return mDragView != null && mDragView.isAnimationFinished();
-            }
-
-            @Override
-            public void onPreDragStart(DropTarget.DragObject dragObject) {
-                mDragView = dragObject.dragView;
-            }
-
-            @Override
-            public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
-                mDragView = null;
-            }
-        };
+        dragOptions.preDragCondition = null;
         if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
             PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
                     mControllers.taskbarPopupController.showForIcon(btv);
@@ -190,6 +174,32 @@
                 dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
             }
         }
+        if (dragOptions.preDragCondition == null) {
+            dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
+                private DragView mDragView;
+
+                @Override
+                public boolean shouldStartDrag(double distanceDragged) {
+                    return mDragView != null && mDragView.isAnimationFinished();
+                }
+
+                @Override
+                public void onPreDragStart(DropTarget.DragObject dragObject) {
+                    mDragView = dragObject.dragView;
+
+                    if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()
+                            && !shouldStartDrag(0)) {
+                        // Immediately close the popup menu.
+                        mDragView.setOnAnimationEndCallback(() -> callOnDragStart());
+                    }
+                }
+
+                @Override
+                public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+                    mDragView = null;
+                }
+            };
+        }
 
         return startDrag(
                 drawable,
@@ -314,13 +324,13 @@
             clipDescription = new ClipDescription(item.title,
                     new String[] {
                             item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
-                                    ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT
-                                    : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY
+                                    ? ClipDescription.MIMETYPE_APPLICATION_SHORTCUT
+                                    : ClipDescription.MIMETYPE_APPLICATION_ACTIVITY
                     });
             intent = new Intent();
             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                 String deepShortcutId = ((WorkspaceItemInfo) item).getDeepShortcutId();
-                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+                intent.putExtra(ClipDescription.EXTRA_PENDING_INTENT,
                         launcherApps.getShortcutIntent(
                                 item.getIntent().getPackage(),
                                 deepShortcutId,
@@ -329,19 +339,19 @@
                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
                 intent.putExtra(Intent.EXTRA_SHORTCUT_ID, deepShortcutId);
             } else {
-                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
-                        LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps,
-                                item.getIntent().getComponent(), null, item.user));
+                intent.putExtra(ClipDescription.EXTRA_PENDING_INTENT,
+                        launcherApps.getMainActivityLaunchIntent(item.getIntent().getComponent(),
+                                null, item.user));
             }
             intent.putExtra(Intent.EXTRA_USER, item.user);
         } else if (tag instanceof Task) {
             Task task = (Task) tag;
             clipDescription = new ClipDescription(task.titleDescription,
                     new String[] {
-                            ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK
+                            ClipDescription.MIMETYPE_APPLICATION_TASK
                     });
             intent = new Intent();
-            intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id);
+            intent.putExtra(Intent.EXTRA_TASK_ID, task.key.id);
             intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId));
         }
 
@@ -393,11 +403,17 @@
         return super.isDragging() || mIsSystemDragInProgress;
     }
 
+    /** {@code true} if the system is currently handling the drag. */
+    public boolean isSystemDragInProgress() {
+        return mIsSystemDragInProgress;
+    }
+
     private void maybeOnDragEnd() {
         if (!isDragging()) {
             ((BubbleTextView) mDragObject.originalView).getIcon().setIsDisabled(false);
             mControllers.taskbarAutohideSuspendController.updateFlag(
                     TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false);
+            mActivity.onDragEnd();
         }
     }
 
@@ -418,23 +434,18 @@
             ItemInfo item = (ItemInfo) tag;
             TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
             if (item.container == CONTAINER_ALL_APPS) {
-                // Since all apps closes when the drag starts, target the all apps button instead
+                // Since all apps closes when the drag starts, target the all apps button instead.
                 target = taskbarViewController.getAllAppsButtonView();
             } else if (item.container >= 0) {
-                // Since folders close when the drag starts, target the folder icon instead
-                LauncherBindableItemsContainer.ItemOperator op = (info, v) -> {
-                    if (info instanceof FolderInfo && v instanceof FolderIcon) {
-                        FolderInfo fi = (FolderInfo) info;
-                        for (WorkspaceItemInfo si : fi.contents) {
-                            if (si.id == item.id) {
-                                // Found the parent
-                                return true;
-                            }
-                        }
-                    }
-                    return false;
-                };
-                target = taskbarViewController.mapOverItems(op);
+                // Since folders close when the drag starts, target the folder icon instead.
+                ItemInfoMatcher matcher = ItemInfoMatcher.forFolderMatch(
+                        ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
+                target = taskbarViewController.getFirstIconMatch(matcher);
+            } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+                // Find first icon with same package/user as the deep shortcut.
+                ItemInfoMatcher packageUserMatcher = ItemInfoMatcher.ofPackages(
+                        Collections.singleton(item.getTargetPackage()), item.user);
+                target = taskbarViewController.getFirstIconMatch(packageUserMatcher);
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 1bd76b9..c7330d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
 import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -181,6 +182,12 @@
             } else if (!mControllers.uiController.isTaskbarTouchable()) {
                 // Let touches pass through us.
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (mControllers.taskbarDragController.isSystemDragInProgress()) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (AbstractFloatingView.getOpenView(mActivity, TYPE_TASKBAR_ALL_APPS) != null) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
             } else if (mControllers.taskbarViewController.areIconsVisible()
                     || AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null
                     || mActivity.isNavBarKidsModeActive()) {
@@ -208,9 +215,7 @@
          * Called when a child is removed from TaskbarDragLayer.
          */
         public void onDragLayerViewRemoved() {
-            if (AbstractFloatingView.getAnyView(mActivity, TYPE_ALL) == null) {
-                mActivity.setTaskbarWindowFullscreen(false);
-            }
+            mActivity.maybeSetTaskbarWindowNotFullscreen();
         }
 
         /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
index 5efcc4d..8e57ea6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
@@ -52,7 +52,7 @@
     }
 
     @Override
-    protected int getChildGap() {
+    protected int getChildGap(int fromIndex, int toIndex) {
         return mTaskbarEduView.getPaddingLeft() + mTaskbarEduView.getPaddingRight();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index 385090f..c99cebb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -71,7 +71,7 @@
     /** Update values tracked via sysui flags. */
     public void updateSysuiFlags(int sysuiFlags) {
         mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_IMMERSIVE_MODE) != 0;
-        if (mContext.isNavBarKidsModeActive()) {
+        if (mContext.isNavBarForceVisible()) {
             if (mIsImmersiveMode) {
                 startIconDimming();
             } else {
@@ -113,7 +113,7 @@
      * Returns whether the taskbar is always visible in immersive mode.
      */
     private boolean isNavbarShownInImmersiveMode() {
-        return mIsImmersiveMode && mContext.isNavBarKidsModeActive();
+        return mIsImmersiveMode && mContext.isNavBarForceVisible();
     }
 
     private void updateIconDimmingAlpha() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index 991bcec..56648ea 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -1,5 +1,6 @@
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
@@ -14,6 +15,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.systemui.shared.system.QuickStepContract;
 
 import java.io.PrintWriter;
@@ -39,6 +41,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             mIsScreenOff = true;
+            AbstractFloatingView.closeOpenViews(mContext, false, TYPE_ALL);
         }
     };
 
@@ -71,6 +74,10 @@
         mNavbarButtonsViewController.setKeyguardVisible(keyguardShowing || dozing,
                 keyguardOccluded);
         updateIconsForBouncer();
+
+        if (keyguardShowing) {
+            AbstractFloatingView.closeOpenViews(mContext, true, TYPE_ALL);
+        }
     }
 
     public boolean isScreenOff() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 0f7abda..494d21e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -21,6 +21,7 @@
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
 
 import android.content.ComponentCallbacks;
@@ -40,15 +41,13 @@
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.quickstep.RecentsActivity;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TouchInteractionService;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -59,8 +58,7 @@
 /**
  * Class to manage taskbar lifecycle
  */
-public class TaskbarManager implements DisplayController.DisplayInfoChangeListener,
-        SysUINavigationMode.NavigationModeChangeListener {
+public class TaskbarManager implements DisplayController.DisplayInfoChangeListener {
 
     private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
             Settings.Secure.USER_SETUP_COMPLETE);
@@ -70,7 +68,6 @@
 
     private final Context mContext;
     private final DisplayController mDisplayController;
-    private final SysUINavigationMode mSysUINavigationMode;
     private final TaskbarNavButtonController mNavButtonController;
     private final SettingsCache.OnChangeListener mUserSetupCompleteListener;
     private final SettingsCache.OnChangeListener mNavBarKidsModeListener;
@@ -78,8 +75,11 @@
     private final SimpleBroadcastReceiver mShutdownReceiver;
 
     // The source for this provider is set when Launcher is available
+    // We use 'non-destroyable' version here so the original provider won't be destroyed
+    // as it is tied to the activity lifecycle, not the taskbar lifecycle.
+    // It's destruction/creation will be managed by the activity.
     private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
-            new ScopedUnfoldTransitionProgressProvider();
+            new NonDestroyableScopedUnfoldTransitionProgressProvider();
 
     private TaskbarActivityContext mTaskbarActivityContext;
     private StatefulActivity mActivity;
@@ -89,14 +89,18 @@
      */
     private final TaskbarSharedState mSharedState = new TaskbarSharedState();
 
-    private static final int CHANGE_FLAGS =
-            CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
+    /**
+     * We use WindowManager's ComponentCallbacks() for most of the config changes, however for
+     * navigation mode, that callback gets called too soon, before it's internal navigation mode
+     * reflects the current one.
+     * DisplayController's callback is delayed enough to get the correct nav mode value
+     */
+    private static final int CHANGE_FLAGS = CHANGE_NAVIGATION_MODE;
 
     private boolean mUserUnlocked = false;
 
     public TaskbarManager(TouchInteractionService service) {
         mDisplayController = DisplayController.INSTANCE.get(service);
-        mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service);
         Display display =
                 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
         mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
@@ -109,19 +113,29 @@
 
             @Override
             public void onConfigurationChanged(Configuration newConfig) {
+                DeviceProfile dp = mUserUnlocked
+                        ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
+                        : null;
                 int configDiff = mOldConfig.diff(newConfig);
                 int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS
-                        | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE;
+                        | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE
+                        | ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_SCREEN_SIZE;
                 if ((configDiff & configsRequiringRecreate) != 0) {
-                    // Color has changed, recreate taskbar to reload background color & icons.
-                    recreateTaskbar();
+                    if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0 &&
+                            mTaskbarActivityContext != null && dp != null) {
+                        DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile();
+                        // Additional check since this callback gets fired multiple times w/o
+                        // screen size changing
+                        if (dp.widthPx != oldDp.widthPx || dp.heightPx != oldDp.heightPx) {
+                            recreateTaskbar();
+                        }
+                    } else {
+                        // Color has changed, recreate taskbar to reload background color & icons.
+                        recreateTaskbar();
+                    }
                 } else {
                     // Config change might be handled without re-creating the taskbar
                     if (mTaskbarActivityContext != null) {
-                        DeviceProfile dp = mUserUnlocked
-                                ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
-                                : null;
-
                         if (dp != null && dp.isTaskbarPresent) {
                             mTaskbarActivityContext.updateDeviceProfile(dp.copy(mContext));
                         }
@@ -137,7 +151,6 @@
         mShutdownReceiver = new SimpleBroadcastReceiver(i -> destroyExistingTaskbar());
 
         mDisplayController.addChangeListener(this);
-        mSysUINavigationMode.addModeChangeListener(this);
         SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
                 mUserSetupCompleteListener);
         SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE,
@@ -149,11 +162,6 @@
     }
 
     @Override
-    public void onNavigationModeChanged(Mode newMode) {
-        recreateTaskbar();
-    }
-
-    @Override
     public void onDisplayInfoChanged(Context context, Info info, int flags) {
         if ((flags & CHANGE_FLAGS) != 0) {
             recreateTaskbar();
@@ -301,7 +309,6 @@
     public void destroy() {
         destroyExistingTaskbar();
         mDisplayController.removeChangeListener(this);
-        mSysUINavigationMode.removeModeChangeListener(this);
         SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
                 mUserSetupCompleteListener);
         SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index ea4fe34..f9c8062 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -145,6 +145,7 @@
                 });
         // TODO (b/198438631): configure for taskbar/context
         container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
+        mControllers.taskbarDragController.addDragListener(container);
 
         container.populateAndShow(icon,
                 mPopupDataProvider.getShortcutCountForItem(item),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index 23beef0..a5c55b0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -24,4 +24,6 @@
 
     public boolean setupUIVisible = false;
 
+    public boolean allAppsVisible = false;
+
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index 1589582..f131595 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -35,7 +35,6 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.LauncherAppsCompat;
 
 import java.util.List;
 
@@ -94,8 +93,7 @@
                         info.user);
             } else {
                 SystemUiProxy.INSTANCE.get(mContext).startIntent(
-                        LauncherAppsCompat.getMainActivityLaunchIntent(
-                                mLauncherApps,
+                        mLauncherApps.getMainActivityLaunchIntent(
                                 item.getIntent().getComponent(),
                                 /* startActivityOptions= */null,
                                 item.user),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 1d5fa4d..54dd0b2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -29,10 +29,12 @@
 import android.content.SharedPreferences;
 import android.view.ViewConfiguration;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.StringJoiner;
@@ -52,6 +54,10 @@
     public static final int FLAG_STASHED_IN_APP_IME = 1 << 5; // IME is visible
     public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6;
     public static final int FLAG_STASHED_IN_APP_ALL_APPS = 1 << 7; // All apps is visible.
+    public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard
+
+    // If any of these flags are enabled, isInApp should return true.
+    private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
 
     // If we're in an app and any of these flags are enabled, taskbar should be stashed.
     private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
@@ -60,15 +66,16 @@
 
     // If any of these flags are enabled, inset apps by our stashed height instead of our unstashed
     // height. This way the reported insets are consistent even during transitions out of the app.
-    // Currently any flag that causes us to stash in an app is included, except for IME since that
-    // covers the underlying app anyway and thus the app shouldn't change insets.
+    // Currently any flag that causes us to stash in an app is included, except for IME or All Apps
+    // since those cover the underlying app anyway and thus the app shouldn't change insets.
     private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP
-            & ~FLAG_STASHED_IN_APP_IME;
+            & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_APP_ALL_APPS;
 
     /**
      * How long to stash/unstash when manually invoked via long press.
      */
-    public static final long TASKBAR_STASH_DURATION = 300;
+    public static final long TASKBAR_STASH_DURATION =
+            WindowManagerWrapper.ANIMATION_DURATION_RESIZE;
 
     /**
      * How long to stash/unstash when keyboard is appearing/disappearing.
@@ -133,10 +140,12 @@
     private boolean mIsSystemGestureInProgress;
     private boolean mIsImeShowing;
 
+    private boolean mEnableManualStashingForTests = false;
+
     // Evaluate whether the handle should be stashed
     private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
             flags -> {
-                boolean inApp = hasAnyFlag(flags, FLAG_IN_APP);
+                boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
                 boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
                 boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
                 return (inApp && stashedInApp) || (!inApp && stashedLauncherState);
@@ -174,10 +183,7 @@
         boolean isInSetup = !mActivity.isUserSetupComplete() || sharedState.setupUIVisible;
         updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
         updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
-        if (isInSetup) {
-            // Update the in-app state to ensure isStashed() reflects right state during SUW
-            updateStateForFlag(FLAG_IN_APP, true);
-        }
+        updateStateForFlag(FLAG_IN_SETUP, isInSetup);
         applyState();
 
         notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
@@ -187,7 +193,7 @@
      * Returns whether the taskbar can visually stash into a handle based on the current device
      * state.
      */
-    private boolean supportsVisualStashing() {
+    public boolean supportsVisualStashing() {
         return !mActivity.isThreeButtonNav();
     }
 
@@ -196,21 +202,25 @@
      */
     protected boolean supportsManualStashing() {
         return supportsVisualStashing()
-                && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
+                && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingForTests);
     }
 
-    private boolean supportsStashingForTests() {
-        // TODO: enable this for tests that specifically check stash/unstash behavior.
-        return false;
+    /**
+     * Enables support for manual stashing. This should only be used to add this functionality
+     * to Launcher specific tests.
+     */
+    public void enableManualStashingForTests(boolean enableManualStashing) {
+        mEnableManualStashingForTests = enableManualStashing;
     }
 
     /**
      * Sets the flag indicating setup UI is visible
      */
     protected void setSetupUIVisible(boolean isVisible) {
-        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP,
-                isVisible || !mActivity.isUserSetupComplete());
-        applyState();
+        boolean hideTaskbar = isVisible || !mActivity.isUserSetupComplete();
+        updateStateForFlag(FLAG_IN_SETUP, hideTaskbar);
+        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, hideTaskbar);
+        applyState(hideTaskbar ? 0 : TASKBAR_STASH_DURATION);
     }
 
     /**
@@ -247,17 +257,35 @@
      * Returns whether the taskbar is currently visible and in an app.
      */
     public boolean isInAppAndNotStashed() {
-        return !mIsStashed && (mState & FLAG_IN_APP) != 0;
+        return !mIsStashed && isInApp();
+    }
+
+    private boolean isInApp() {
+        return hasAnyFlag(FLAGS_IN_APP);
     }
 
     /**
      * Returns the height that taskbar will inset when inside apps.
      */
     public int getContentHeightToReportToApps() {
-        if (hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
+        if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
+            DeviceProfile dp = mActivity.getDeviceProfile();
+            if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent && !dp.isLandscape) {
+                // We always show the back button in SUW but in portrait the SUW layout may not
+                // be wide enough to support overlapping the nav bar with its content.  For now,
+                // just inset by the bar height.
+                return mUnstashedHeight;
+            }
             boolean isAnimating = mAnimator != null && mAnimator.isStarted();
-            return mControllers.stashedHandleViewController.isStashedHandleVisible() || isAnimating
-                    ? mStashedHeight : 0;
+            if (!mControllers.stashedHandleViewController.isStashedHandleVisible()
+                    && isInApp()
+                    && !isAnimating) {
+                // We are in a settled state where we're not showing the handle even though taskbar
+                // is stashed. This can happen for example when home button is disabled (see
+                // StashedHandleViewController#setIsHomeButtonDisabled()).
+                return 0;
+            }
+            return mStashedHeight;
         }
         return mUnstashedHeight;
     }
@@ -452,7 +480,7 @@
     }
 
     public void applyState() {
-        applyState(TASKBAR_STASH_DURATION);
+        applyState(hasAnyFlag(FLAG_IN_SETUP) ? 0 : TASKBAR_STASH_DURATION);
     }
 
     public void applyState(long duration) {
@@ -542,8 +570,8 @@
         if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP)) {
             mControllers.uiController.onStashedInAppChanged();
         }
-        if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAG_IN_APP)) {
-            notifyStashChange(/* visible */ hasAnyFlag(FLAG_IN_APP),
+        if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAGS_IN_APP)) {
+            notifyStashChange(/* visible */ hasAnyFlag(FLAGS_IN_APP),
                             /* stashed */ isStashedInApp());
         }
         if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_MANUAL)) {
@@ -587,6 +615,7 @@
         str.add((flags & FLAG_IN_STASHED_LAUNCHER_STATE) != 0
                 ? "FLAG_IN_STASHED_LAUNCHER_STATE" : "");
         str.add((flags & FLAG_STASHED_IN_APP_ALL_APPS) != 0 ? "FLAG_STASHED_IN_APP_ALL_APPS" : "");
+        str.add((flags & FLAG_IN_SETUP) != 0 ? "FLAG_IN_SETUP" : "");
         return str.toString();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 07b851f..6bc2a93 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -68,4 +68,11 @@
     public void setSystemGestureInProgress(boolean inProgress) {
         mControllers.taskbarStashController.setSystemGestureInProgress(inProgress);
     }
+
+    /**
+     * Manually closes the all apps window.
+     */
+    public void hideAllApps() {
+        mControllers.taskbarAllAppsController.hide();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
index d5ea570..64a4fa7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -15,12 +15,14 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.view.IWindowManager;
 import android.view.View;
 import android.view.WindowManager;
 
 import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
@@ -31,14 +33,18 @@
 public class TaskbarUnfoldAnimationController implements
         TaskbarControllers.LoggableTaskbarController {
 
-    private final ScopedUnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+    private final ScopedUnfoldTransitionProgressProvider mScopedUnfoldTransitionProgressProvider;
+    private final NaturalRotationUnfoldProgressProvider mNaturalUnfoldTransitionProgressProvider;
     private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimator;
     private final TransitionListener mTransitionListener = new TransitionListener();
     private TaskbarViewController mTaskbarViewController;
 
-    public TaskbarUnfoldAnimationController(ScopedUnfoldTransitionProgressProvider
-            unfoldTransitionProgressProvider, WindowManager windowManager) {
-        mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider;
+    public TaskbarUnfoldAnimationController(BaseTaskbarContext context,
+            ScopedUnfoldTransitionProgressProvider source,
+            WindowManager windowManager, IWindowManager iWindowManager) {
+        mScopedUnfoldTransitionProgressProvider = source;
+        mNaturalUnfoldTransitionProgressProvider =
+                new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source);
         mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
                 new LauncherViewsMoveFromCenterTranslationApplier());
     }
@@ -48,18 +54,21 @@
      * @param taskbarControllers references to all other taskbar controllers
      */
     public void init(TaskbarControllers taskbarControllers) {
+        mNaturalUnfoldTransitionProgressProvider.init();
         mTaskbarViewController = taskbarControllers.taskbarViewController;
         mTaskbarViewController.addOneTimePreDrawListener(() ->
-                mUnfoldTransitionProgressProvider.setReadyToHandleTransition(true));
-        mUnfoldTransitionProgressProvider.addCallback(mTransitionListener);
+                mScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(true));
+        mNaturalUnfoldTransitionProgressProvider.addCallback(mTransitionListener);
     }
 
     /**
      * Destroys the controller
      */
     public void onDestroy() {
-        mUnfoldTransitionProgressProvider.setReadyToHandleTransition(false);
-        mUnfoldTransitionProgressProvider.removeCallback(mTransitionListener);
+        mScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(false);
+        mNaturalUnfoldTransitionProgressProvider.removeCallback(mTransitionListener);
+        mNaturalUnfoldTransitionProgressProvider.destroy();
+        mTaskbarViewController = null;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index ade58a9..0b537e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.AllAppsButton;
@@ -367,17 +368,36 @@
     }
 
     /**
-     * Maps {@code op} over all the child views, returning the view that {@code op} evaluates
-     * {@code true} for, or {@code null} if none satisfy {@code op}.
+     * Maps {@code op} over all the child views.
      */
-    protected View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
         // map over all the shortcuts on the taskbar
         for (int i = 0; i < getChildCount(); i++) {
             View item = getChildAt(i);
             if (op.evaluate((ItemInfo) item.getTag(), item)) {
-                return item;
+                return;
             }
         }
-        return null;
+    }
+
+    /**
+     * Finds the first icon to match one of the given matchers, from highest to lowest priority.
+     * @return The first match, or All Apps button if no match was found.
+     */
+    public View getFirstMatch(ItemInfoMatcher... matchers) {
+        for (ItemInfoMatcher matcher : matchers) {
+            for (int i = 0; i < getChildCount(); i++) {
+                View item = getChildAt(i);
+                if (!(item.getTag() instanceof ItemInfo)) {
+                    // Should only happen for All Apps button.
+                    continue;
+                }
+                ItemInfo info = (ItemInfo) item.getTag();
+                if (matcher.matchesInfo(info)) {
+                    return item;
+                }
+            }
+        }
+        return mAllAppsButton;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 153ed14..a89061b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -20,13 +20,14 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.AnimatedFloat.VALUE;
 
+import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.FloatProperty;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
+
+import androidx.core.view.OneShotPreDrawListener;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
@@ -37,6 +38,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
@@ -142,18 +144,8 @@
      * drawing a frame and invoked only once
      * @param listener callback that will be invoked before drawing the next frame
      */
-    public void addOneTimePreDrawListener(Runnable listener) {
-        mTaskbarView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                final ViewTreeObserver viewTreeObserver = mTaskbarView.getViewTreeObserver();
-                if (viewTreeObserver.isAlive()) {
-                    listener.run();
-                    viewTreeObserver.removeOnPreDrawListener(this);
-                }
-                return true;
-            }
-        });
+    public void addOneTimePreDrawListener(@NonNull Runnable listener) {
+        OneShotPreDrawListener.add(mTaskbarView, listener);
     }
 
     public Rect getIconLayoutBounds() {
@@ -274,8 +266,22 @@
         mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
     }
 
-    public View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
-        return mTaskbarView.mapOverItems(op);
+    /**
+     * Maps the given operator to all the top-level children of TaskbarView.
+     */
+    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+        mTaskbarView.mapOverItems(op);
+    }
+
+    /**
+     * Returns the first icon to match the given parameter, in priority from:
+     * 1) Icons directly on Taskbar
+     * 2) FolderIcon of the Folder containing the given icon
+     * 3) All Apps button
+     */
+    public View getFirstIconMatch(ItemInfoMatcher matcher) {
+        ItemInfoMatcher folderMatcher = ItemInfoMatcher.forFolderMatch(matcher);
+        return mTaskbarView.getFirstMatch(matcher, folderMatcher);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index b36b9f1..0ea2aa0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -17,19 +17,17 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowInsets;
 
-import androidx.recyclerview.widget.RecyclerView;
-
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.BaseAllAppsContainerView;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.BaseAdapterProvider;
+import com.android.launcher3.allapps.BaseAllAppsAdapter;
 
 /** All apps container accessible from taskbar. */
-public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView<TaskbarAllAppsContext> {
+public class TaskbarAllAppsContainerView extends
+        ActivityAllAppsContainerView<TaskbarAllAppsContext> {
 
     public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -40,43 +38,15 @@
     }
 
     @Override
-    protected SearchAdapterProvider<?> createMainAdapterProvider() {
-        // Taskbar all apps does not yet support search, so this implementation is minimal.
-        return new SearchAdapterProvider<TaskbarAllAppsContext>(mActivityContext) {
-            @Override
-            public boolean launchHighlightedItem() {
-                return false;
-            }
-
-            @Override
-            public View getHighlightedItem() {
-                return null;
-            }
-
-            @Override
-            public RecyclerView.ItemDecoration getDecorator() {
-                return null;
-            }
-
-            @Override
-            public boolean isViewSupported(int viewType) {
-                return false;
-            }
-
-            @Override
-            public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) { }
-
-            @Override
-            public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
-                    ViewGroup parent, int viewType) {
-                return null;
-            }
-        };
-    }
-
-    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
         return super.onApplyWindowInsets(insets);
     }
+
+    @Override
+    protected BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<TaskbarAllAppsContext> mAppsList,
+            BaseAdapterProvider[] adapterProviders) {
+        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
+                adapterProviders);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
index 4245119..50dfff0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
@@ -19,14 +19,22 @@
 import static android.view.KeyEvent.KEYCODE_BACK;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
 import android.content.Context;
+import android.graphics.Insets;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.WindowInsets;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
@@ -34,9 +42,14 @@
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarDragController;
 import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
 
 /**
  * Window context for the taskbar all apps overlay.
@@ -48,11 +61,16 @@
     private final TaskbarActivityContext mTaskbarContext;
     private final OnboardingPrefs<TaskbarAllAppsContext> mOnboardingPrefs;
 
+    private final TaskbarAllAppsController mWindowController;
     private final TaskbarAllAppsViewController mAllAppsViewController;
     private final TaskbarDragController mDragController;
     private final TaskbarAllAppsDragLayer mDragLayer;
     private final TaskbarAllAppsContainerView mAppsView;
 
+    // We automatically stash taskbar when all apps is opened in gesture navigation mode.
+    private final boolean mWillTaskbarBeVisuallyStashed;
+    private final int mStashedTaskbarHeight;
+
     TaskbarAllAppsContext(
             TaskbarActivityContext taskbarContext,
             TaskbarAllAppsController windowController,
@@ -66,12 +84,16 @@
         mDragLayer = new TaskbarAllAppsDragLayer(this);
         TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
                 R.layout.taskbar_all_apps, mDragLayer, false);
+        mWindowController = windowController;
         mAllAppsViewController = new TaskbarAllAppsViewController(
                 this,
                 slideInView,
                 windowController,
                 taskbarStashController);
         mAppsView = slideInView.getAppsView();
+
+        mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
+        mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
     }
 
     TaskbarAllAppsViewController getAllAppsViewController() {
@@ -128,10 +150,22 @@
     public void onDragStart() {}
 
     @Override
+    public void onDragEnd() {
+        mWindowController.maybeCloseWindow();
+    }
+
+    @Override
     public void onPopupVisibilityChanged(boolean isVisible) {}
 
+    @Override
+    public SearchAdapterProvider<?> createSearchAdapterProvider(
+            ActivityAllAppsContainerView<?> appsView) {
+        return new DefaultSearchAdapterProvider(this);
+    }
+
     /** Root drag layer for this context. */
-    private static class TaskbarAllAppsDragLayer extends BaseDragLayer<TaskbarAllAppsContext> {
+    private static class TaskbarAllAppsDragLayer extends
+            BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInsetsListener {
 
         private TaskbarAllAppsDragLayer(Context context) {
             super(context, null, 1);
@@ -142,7 +176,14 @@
         @Override
         protected void onAttachedToWindow() {
             super.onAttachedToWindow();
-            mActivity.mAllAppsViewController.show();
+            ViewTreeObserverWrapper.addOnComputeInsetsListener(
+                    getViewTreeObserver(), this);
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            ViewTreeObserverWrapper.removeOnComputeInsetsListener(this);
         }
 
         @Override
@@ -151,6 +192,12 @@
         }
 
         @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
         public boolean dispatchKeyEvent(KeyEvent event) {
             if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
                 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
@@ -160,5 +207,45 @@
             }
             return super.dispatchKeyEvent(event);
         }
+
+        @Override
+        public void onComputeInsets(InsetsInfo inoutInfo) {
+            if (mActivity.mDragController.isSystemDragInProgress()) {
+                inoutInfo.touchableRegion.setEmpty();
+                inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            }
+        }
+
+        @Override
+        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+            return updateInsetsDueToStashing(insets);
+        }
+
+        /**
+         * Taskbar automatically stashes when opening all apps, but we don't report the insets as
+         * changing to avoid moving the underlying app. But internally, the apps view should still
+         * layout according to the stashed insets rather than the unstashed insets. So this method
+         * does two things:
+         * 1) Sets navigationBars bottom inset to stashedHeight.
+         * 2) Sets tappableInsets bottom inset to 0.
+         */
+        private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
+            if (!mActivity.mWillTaskbarBeVisuallyStashed) {
+                return oldInsets;
+            }
+            WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+            Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+            Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+                    mActivity.mStashedTaskbarHeight);
+            updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+
+            Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+            Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+                    oldTappableInsets.right, 0);
+            updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+
+            return updatedInsetsBuilder.build();
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 9302452..9fca8eb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -15,8 +15,11 @@
  */
 package com.android.launcher3.taskbar.allapps;
 
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.view.Gravity;
@@ -35,6 +38,9 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarSharedState;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.util.List;
 import java.util.Optional;
@@ -58,7 +64,15 @@
     private final TaskbarAllAppsProxyView mProxyView;
     private final LayoutParams mLayoutParams;
 
+    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+        @Override
+        public void onTaskStackChanged() {
+            mProxyView.close(false);
+        }
+    };
+
     private TaskbarControllers mControllers;
+    private TaskbarSharedState mSharedState;
     /** Window context for all apps if it is open. */
     private @Nullable TaskbarAllAppsContext mAllAppsContext;
 
@@ -74,9 +88,19 @@
     }
 
     /** Initialize the controller. */
-    public void init(TaskbarControllers controllers) {
-        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            mControllers = controllers;
+    public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
+        if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            return;
+        }
+        mControllers = controllers;
+        mSharedState = sharedState;
+
+        /*
+         * Recreate All Apps if it was open in the previous Taskbar instance (e.g. the configuration
+         * changed).
+         */
+        if (mSharedState.allAppsVisible) {
+            show(false);
         }
     }
 
@@ -109,16 +133,22 @@
 
     /** Opens the {@link TaskbarAllAppsContainerView} in a new window. */
     public void show() {
+        show(true);
+    }
+
+    private void show(boolean animate) {
         if (mProxyView.isOpen()) {
             return;
         }
         mProxyView.show();
+        mSharedState.allAppsVisible = true;
 
         mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext,
                 this,
                 mControllers.taskbarStashController);
         mAllAppsContext.getDragController().init(mControllers);
         mTaskbarContext.addOnDeviceProfileChangeListener(this);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
         Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
                 .ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
 
@@ -126,15 +156,33 @@
         mAllAppsContext.getAppsView().getFloatingHeaderView()
                 .findFixedRowByType(PredictionRowView.class)
                 .setPredictedApps(mPredictedApps);
+        mAllAppsContext.getAllAppsViewController().show(animate);
+    }
+
+    /** Closes the {@link TaskbarAllAppsContainerView}. */
+    public void hide() {
+        mProxyView.close(true);
     }
 
     /**
-     * Removes the all apps window from the hierarchy.
+     * Removes the all apps window from the hierarchy, if all floating views are closed and there is
+     * no system drag operation in progress.
      * <p>
      * This method should be called after an exit animation finishes, if applicable.
      */
-    void closeWindow() {
+    void maybeCloseWindow() {
+        if (AbstractFloatingView.getOpenView(mAllAppsContext, TYPE_ALL) != null
+                || mAllAppsContext.getDragController().isSystemDragInProgress()) {
+            return;
+        }
         mProxyView.close(false);
+        mSharedState.allAppsVisible = false;
+        onDestroy();
+    }
+
+    /** Destroys the controller and any All Apps window if present. */
+    public void onDestroy() {
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
         mTaskbarContext.removeOnDeviceProfileChangeListener(this);
         Optional.ofNullable(mAllAppsContext)
                 .map(c -> c.getSystemService(WindowManager.class))
@@ -151,6 +199,7 @@
         layoutParams.gravity = Gravity.BOTTOM;
         layoutParams.packageName = mTaskbarContext.getPackageName();
         layoutParams.setFitInsetsTypes(0); // Handled by container view.
+        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         layoutParams.setSystemApplicationOverlay(true);
         return layoutParams;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java
new file mode 100644
index 0000000..53fe06d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsFallbackSearchContainer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.SearchUiManager;
+
+/** Empty search container for Taskbar All Apps used as a fallback if search is not supported. */
+public class TaskbarAllAppsFallbackSearchContainer extends View implements SearchUiManager {
+    public TaskbarAllAppsFallbackSearchContainer(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskbarAllAppsFallbackSearchContainer(
+            Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void initializeSearch(ActivityAllAppsContainerView<?> containerView) {
+        // Do nothing.
+    }
+
+    @Override
+    public void resetSearch() {
+        // Do nothing.
+    }
+
+    @Nullable
+    @Override
+    public ExtendedEditText getEditText() {
+        return null;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 02aa3f2..5d2d72a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -50,17 +50,21 @@
     }
 
     /** Opens the all apps view. */
-    void show() {
+    void show(boolean animate) {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
             return;
         }
         mIsOpen = true;
         attachToContainer();
 
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-        mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
-        mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
+        if (animate) {
+            mOpenCloseAnimator.setValues(
+                    PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+            mOpenCloseAnimator.setInterpolator(AGGRESSIVE_EASE);
+            mOpenCloseAnimator.setDuration(DEFAULT_OPEN_DURATION).start();
+        } else {
+            mTranslationShift = TRANSLATION_SHIFT_OPENED;
+        }
     }
 
     /** The apps container inside this view. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index c1abaac..32ebbe8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView.DEFAULT_OPEN_DURATION;
 import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.appprediction.AppsDividerView;
 import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.taskbar.TaskbarStashController;
@@ -49,12 +50,12 @@
         setUpIconLongClick();
         setUpAppDivider();
         setUpTaskbarStashing();
-        mSlideInView.addOnCloseListener(windowController::closeWindow);
+        mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
     }
 
     /** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
-    void show() {
-        mSlideInView.show();
+    void show(boolean animate) {
+        mSlideInView.show(animate);
     }
 
     /** Closes the {@link TaskbarAllAppsSlideInView}. */
@@ -83,6 +84,8 @@
         mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, true);
         mTaskbarStashController.applyState(DEFAULT_OPEN_DURATION);
         mSlideInView.setOnCloseBeginListener(() -> {
+            AbstractFloatingView.closeOpenContainer(
+                    mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
             mTaskbarStashController.updateStateForFlag(
                     FLAG_STASHED_IN_APP_ALL_APPS, false);
             mTaskbarStashController.applyState(DEFAULT_CLOSE_DURATION);
diff --git a/quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java b/quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java
new file mode 100644
index 0000000..f9da4e4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.unfold;
+
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
+
+/**
+ * ScopedUnfoldTransitionProgressProvider that doesn't propagate destroy method
+ */
+public class NonDestroyableScopedUnfoldTransitionProgressProvider extends
+        ScopedUnfoldTransitionProgressProvider {
+
+    @Override
+    public void destroy() {
+        // no-op
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index aa26645..2f8e4d9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -20,12 +20,11 @@
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
-import android.view.Display;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 
 public class ApiWrapper {
 
@@ -37,24 +36,10 @@
     }
 
     /**
-     * Returns true if the display is an internal displays
-     */
-    public static boolean isInternalDisplay(Display display) {
-        return display.getType() == Display.TYPE_INTERNAL;
-    }
-
-    /**
-     * Returns a unique ID representing the display
-     */
-    public static String getUniqueId(Display display) {
-        return display.getUniqueId();
-    }
-
-    /**
      * Returns the minimum space that should be left empty at the end of hotseat
      */
     public static int getHotseatEndOffset(Context context) {
-        if (SysUINavigationMode.INSTANCE.get(context).getMode() == Mode.THREE_BUTTONS) {
+        if (DisplayController.getNavigationMode(context) == NavigationMode.THREE_BUTTONS) {
             Resources res = context.getResources();
             /*
             * 3 nav buttons +
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 0eaea83..84b3839 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -26,12 +26,14 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.util.FloatProperty;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 
@@ -65,7 +67,10 @@
         ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
         TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
 
-        getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+        float recentsAlpha = state.overviewUi ? 1f : 0;
+        Log.d(BAD_STATE, "BaseRecentsViewStateController setState state=" + state
+                + ", alpha=" + recentsAlpha);
+        getContentAlphaProperty().set(mRecentsView, recentsAlpha);
         getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
         RECENTS_GRID_PROGRESS.set(mRecentsView,
                 state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
@@ -74,6 +79,8 @@
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation builder) {
+        Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimation state=" + toState
+                + ", config.skipOverview=" + config.hasAnimationFlag(SKIP_OVERVIEW));
         if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
             return;
         }
@@ -97,7 +104,10 @@
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
-        setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
+        float recentsAlpha = toState.overviewUi ? 1 : 0;
+        Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimationInternal toState="
+                + toState + ", alpha=" + recentsAlpha);
+        setter.setFloat(mRecentsView, getContentAlphaProperty(), recentsAlpha,
                 config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
 
         setter.setFloat(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index c058aa7..08d147f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -21,6 +21,7 @@
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Pair;
@@ -58,6 +59,12 @@
         Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
         ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
                 .getActivityLaunchOptions(hostView);
+        Object itemInfo = hostView.getTag();
+        IBinder launchCookie = null;
+        if (itemInfo instanceof ItemInfo) {
+            launchCookie = mLauncher.getLaunchCookie((ItemInfo) itemInfo);
+            activityOptions.options.setLaunchCookie(launchCookie);
+        }
         if (Utilities.ATLEAST_S && !pendingIntent.isActivity()) {
             // In the event this pending intent eventually launches an activity, i.e. a trampoline,
             // use the Quickstep transition animation.
@@ -65,17 +72,14 @@
                 ActivityTaskManager.getService()
                         .registerRemoteAnimationForNextActivityStart(
                                 pendingIntent.getCreatorPackage(),
-                                activityOptions.options.getRemoteAnimationAdapter());
+                                activityOptions.options.getRemoteAnimationAdapter(),
+                                launchCookie);
             } catch (RemoteException e) {
                 // Do nothing.
             }
         }
         activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
-        Object itemInfo = hostView.getTag();
-        if (itemInfo instanceof ItemInfo) {
-            mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
-        }
+        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
         options = Pair.create(options.first, activityOptions.options);
         if (pendingIntent.isActivity()) {
             logAppLaunch(itemInfo);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 1073d76..829accc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -68,14 +68,14 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
 import com.android.launcher3.widget.LauncherAppWidgetHost;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.QuickstepOnboardingPrefs;
@@ -303,7 +303,7 @@
 
     @Override
     public TouchController[] createTouchControllers() {
-        Mode mode = SysUINavigationMode.getMode(this);
+        NavigationMode mode = DisplayController.getNavigationMode(this);
 
         ArrayList<TouchController> list = new ArrayList<>();
         list.add(getDragController());
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 32ce1c4..947d3e2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -118,8 +118,8 @@
 
         if (isSplitSelectionState(currentState, toState)) {
             // Animation to "dismiss" selected taskView
-            PendingAnimation splitSelectInitAnimation =
-                    mRecentsView.createSplitSelectInitAnimation();
+            PendingAnimation splitSelectInitAnimation = mRecentsView.createSplitSelectInitAnimation(
+                    toState.getTransitionDuration(mLauncher));
             // Add properties to shift remaining taskViews to get out of placeholder view
             splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
                     toState.getSplitSelectTranslation(mLauncher), LINEAR);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 4583fc3..cd14b3f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -38,7 +38,7 @@
 
     @Override
     public int getTransitionDuration(Context context) {
-        return 320;
+        return 150;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index bb13329..236454e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -26,8 +26,8 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Themes;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -58,7 +58,7 @@
     @Override
     public int getTransitionDuration(Context context) {
         // In gesture modes, overview comes in all the way from the side, so give it more time.
-        return SysUINavigationMode.INSTANCE.get(context).getMode().hasGestures ? 380 : 250;
+        return DisplayController.getNavigationMode(context).hasGestures ? 380 : 250;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index bd7e5de..4d05349 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -60,7 +60,7 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.SysUINavigationMode;
+import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.views.RecentsView;
 
@@ -97,7 +97,7 @@
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
 
-            if (SysUINavigationMode.getMode(mActivity).hasGestures
+            if (DisplayController.getNavigationMode(mActivity).hasGestures
                     && overview.getTaskViewCount() > 0) {
                 // Overview is going offscreen, so keep it at its current scale and opacity.
                 config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
@@ -139,7 +139,7 @@
             }
         } else if ((fromState == NORMAL || fromState == HINT_STATE
                 || fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
-            if (SysUINavigationMode.getMode(mActivity).hasGestures) {
+            if (DisplayController.getNavigationMode(mActivity).hasGestures) {
                 config.setInterpolator(ANIM_WORKSPACE_SCALE,
                         fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
                 config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index f6148a7..2ca59eb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -38,9 +38,10 @@
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
@@ -54,6 +55,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.graphics.PointF;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
@@ -224,6 +226,7 @@
         // Set RecentView's initial properties.
         RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
         ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
+        Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators setContentAlpha=1");
         mRecentsView.setContentAlpha(1);
         mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
         mLauncher.getActionsView().getVisibilityAlpha().setValue(
@@ -242,6 +245,24 @@
                 QUICK_SWITCH.getWorkspaceScrimColor(mLauncher), LINEAR);
         if (mRecentsView.getTaskViewCount() == 0) {
             xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
+            Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators from: 0 to: 1");
+            xAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onStart");
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    float alpha = mRecentsView == null ? -1 : CONTENT_ALPHA.get(mRecentsView);
+                    Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onCancel, alpha=" + alpha);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    Log.d(BAD_STATE, "NBQSTC setupOverviewAnimators onEnd");
+                }
+            });
         }
         mXOverviewAnim = xAnim.createPlaybackController();
         mXOverviewAnim.dispatchOnStart();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index f0ef9cc..d1b0a9c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -29,6 +29,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -36,6 +37,7 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
+import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Launcher;
@@ -44,8 +46,8 @@
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.views.RecentsView;
@@ -112,6 +114,7 @@
         RECENTS_SCALE_PROPERTY.set(mOverviewPanel,
                 QUICK_SWITCH.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f);
         ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f);
+        Log.d(BAD_STATE, "QuickSwitchTouchController initCurrentAnimation setContentAlpha=1");
         mOverviewPanel.setContentAlpha(1);
 
         mCurrentAnimation = mLauncher.getStateManager()
@@ -125,7 +128,7 @@
     private void setupInterpolators(StateAnimationConfig stateAnimationConfig) {
         stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
         stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
-        if (SysUINavigationMode.getMode(mLauncher) == Mode.NO_BUTTON) {
+        if (DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
             // Overview lives to the left of workspace, so translate down later than over
             stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
             stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 308bca6..ca7f633 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -38,10 +38,10 @@
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.FlingBlockCheck;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.VibratorWrapper;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -177,7 +177,7 @@
                         // - It's the focused task if in grid view
                         // - The task is snapped
                         mAllowGoingDown = i == mRecentsView.getCurrentPage()
-                                && SysUINavigationMode.getMode(mActivity).hasGestures
+                                && DisplayController.getNavigationMode(mActivity).hasGestures
                                 && (!mRecentsView.showAsGrid() || mTaskBeingDragged.isFocusedTask())
                                 && mRecentsView.isTaskInExpectedScrollPosition(i);
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 2cb7100..43be051 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -32,10 +32,10 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
 import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -833,12 +833,9 @@
         // Notify when the animation starts
         flushOnRecentsAnimationAndLauncherBound();
 
-        // Start hiding the divider
-        setDividerShown(false /* shown */, false /* immediate */);
-
         // Only add the callback to enable the input consumer after we actually have the controller
         mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
-                mRecentsAnimationController::enableInputConsumer);
+                this::startInterceptingTouchesForGesture);
         mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
 
         mPassedOverviewThreshold = false;
@@ -1276,8 +1273,7 @@
             HomeAnimationFactory homeAnimFactory =
                     createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
                             runningTaskTarget);
-            mIsSwipingPipToHome = !mIsSwipeForStagedSplit
-                    && homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
+            mIsSwipingPipToHome = !mIsSwipeForStagedSplit && appCanEnterPip;
             final RectFSpringAnim[] windowAnim;
             if (mIsSwipingPipToHome) {
                 mSwipePipToHomeAnimator = createWindowAnimationToPip(
@@ -1457,6 +1453,17 @@
         return swipePipToHomeAnimator;
     }
 
+    private void startInterceptingTouchesForGesture() {
+        if (mRecentsAnimationController == null) {
+            return;
+        }
+
+        mRecentsAnimationController.enableInputConsumer();
+
+        // Start hiding the divider
+        setDividerShown(false /* shown */, true /* immediate */);
+    }
+
     private void computeRecentsScrollIfInvisible() {
         if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
             // Views typically don't compute scroll when invisible as an optimization,
@@ -1760,6 +1767,7 @@
     private void maybeFinishSwipeToHome() {
         if (mIsSwipingPipToHome && mSwipePipToHomeAnimators[0] != null) {
             SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
+                    mSwipePipToHomeAnimator.getTaskId(),
                     mSwipePipToHomeAnimator.getComponentName(),
                     mSwipePipToHomeAnimator.getDestinationBounds(),
                     mSwipePipToHomeAnimator.getContentOverlay());
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 0f707c0..9686510 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -19,9 +19,9 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.util.DisplayController.getNavigationMode;
 import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
-import static com.android.quickstep.SysUINavigationMode.getMode;
 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
@@ -55,9 +55,10 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.WindowBounds;
 import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.SplitScreenBounds;
@@ -68,6 +69,7 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.HashMap;
+import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -192,7 +194,12 @@
         activity.getStateManager().moveToRestState();
     }
 
-    public void closeOverlay() { }
+    /**
+     * Closes any overlays.
+     */
+    public void closeOverlay() {
+        Optional.ofNullable(getTaskbarController()).ifPresent(TaskbarUIController::hideAllApps);
+    }
 
     public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
             Runnable runnable) {
@@ -355,10 +362,9 @@
 
     /** Gets the space that the overview actions will take, including bottom margin. */
     private int getOverviewActionsHeight(Context context, DeviceProfile dp) {
-        Resources res = context.getResources();
-        return OverviewActionsView.getOverviewActionsBottomMarginPx(getMode(context), dp)
-                + OverviewActionsView.getOverviewActionsTopMarginPx(getMode(context), dp)
-                + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+        return OverviewActionsView.getOverviewActionsBottomMarginPx(getNavigationMode(context), dp)
+                + OverviewActionsView.getOverviewActionsTopMarginPx(getNavigationMode(context), dp)
+                + dp.overviewActionsHeight;
     }
 
     /**
@@ -480,7 +486,7 @@
             // Creating the activity controller animation sometimes reapplies the launcher state
             // (because we set the animation as the current state animation), so we reapply the
             // attached state here as well to ensure recents is shown/hidden appropriately.
-            if (SysUINavigationMode.getMode(mActivity) == Mode.NO_BUTTON) {
+            if (DisplayController.getNavigationMode(mActivity) == NavigationMode.NO_BUTTON) {
                 setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
             }
         }
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index ffdfa43..7feec2c 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -16,7 +16,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
 import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -33,6 +33,7 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.taskbar.FallbackTaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.ActivityInitListener;
@@ -62,8 +63,7 @@
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
             PagedOrientationHandler orientationHandler) {
         calculateTaskSize(context, dp, outRect);
-        if (dp.isVerticalBarLayout()
-                && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
+        if (dp.isVerticalBarLayout() && DisplayController.getNavigationMode(context) != NO_BUTTON) {
             return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
             return dp.heightPx - outRect.bottom;
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index a82137e..a62e9d1 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -50,12 +50,15 @@
 import android.view.SurfaceControl.Transaction;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RectFSpringAnim;
@@ -92,6 +95,8 @@
     private final Matrix mTmpMatrix = new Matrix();
     private float mMaxLauncherScale = 1;
 
+    private boolean mAppCanEnterPip;
+
     public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
             boolean continuingLastGesture, InputConsumerController inputConsumer) {
@@ -134,16 +139,27 @@
     protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
             long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
             RemoteAnimationTargetCompat runningTaskTarget) {
+        mAppCanEnterPip = appCanEnterPip;
+        if (appCanEnterPip) {
+            return new FallbackPipToHomeAnimationFactory();
+        }
         mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
+        startHomeIntent(mActiveAnimationFactory);
+        return mActiveAnimationFactory;
+    }
+
+    private void startHomeIntent(
+            @Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory) {
         ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
         Intent intent = new Intent(mGestureState.getHomeIntent());
-        mActiveAnimationFactory.addGestureContract(intent);
+        if (gestureContractAnimationFactory != null) {
+            gestureContractAnimationFactory.addGestureContract(intent);
+        }
         try {
             mContext.startActivity(intent, options.toBundle());
         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
             mContext.startActivity(createHomeIntent());
         }
-        return mActiveAnimationFactory;
     }
 
     @Override
@@ -159,8 +175,20 @@
 
     @Override
     protected void finishRecentsControllerToHome(Runnable callback) {
+        final Runnable recentsCallback;
+        if (mAppCanEnterPip) {
+            // Make sure Launcher is resumed after auto-enter-pip transition to actually trigger
+            // the PiP task appearing.
+            recentsCallback = () -> {
+                callback.run();
+                startHomeIntent(null /* gestureContractAnimationFactory */);
+            };
+        } else {
+            recentsCallback = callback;
+        }
+        mRecentsView.cleanupRemoteTargets();
         mRecentsAnimationController.finish(
-                false /* toRecents */, callback, true /* sendUserLeaveHint */);
+                mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */);
     }
 
     @Override
@@ -176,7 +204,7 @@
     @Override
     protected void notifyGestureAnimationStartToRecents() {
         if (mRunningOverHome) {
-            if (SysUINavigationMode.getMode(mContext).hasGestures) {
+            if (DisplayController.getNavigationMode(mContext).hasGestures) {
                 mRecentsView.onGestureAnimationStartOnHome(
                         new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()});
             }
@@ -185,6 +213,17 @@
         }
     }
 
+    private class FallbackPipToHomeAnimationFactory extends HomeAnimationFactory {
+        @NonNull
+        @Override
+        public AnimatorPlaybackController createActivityAnimationToHome() {
+            // copied from {@link LauncherSwipeHandlerV2.LauncherHomeAnimationFactory}
+            long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+            return mActivity.getStateManager().createAnimationToNewWorkspace(
+                    RecentsState.HOME, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
+        }
+    }
+
     private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
         private final Rect mTempRect = new Rect();
         private final TransformParams mHomeAlphaParams = new TransformParams();
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 38331a9..10a3a2e 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -44,8 +44,9 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.quickstep.GestureState.GestureEndTarget;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.LayoutUtils;
@@ -72,7 +73,8 @@
     public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
             PagedOrientationHandler orientationHandler) {
         calculateTaskSize(context, dp, outRect);
-        if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
+        if (dp.isVerticalBarLayout()
+                && DisplayController.getNavigationMode(context) != NavigationMode.NO_BUTTON) {
             return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
             return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
@@ -278,6 +280,7 @@
 
     @Override
     public void closeOverlay() {
+        super.closeOverlay();
         Launcher launcher = getCreatedActivity();
         if (launcher == null) {
             return;
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
new file mode 100644
index 0000000..7abcbdb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
+import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Pair;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.BackEvent;
+import android.window.IOnBackInvokedCallback;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.QuickstepTransitionManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.util.RectFSpringAnim;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+/**
+ * Controls the animation of swiping back and returning to launcher.
+ *
+ * This is a two part animation. The first part is an animation that tracks gesture location to
+ * scale and move the leaving app window. Once the gesture is committed, the second part takes over
+ * the app window and plays the rest of app close transitions in one go.
+ *
+ * This animation is used only for apps that enable back dispatching via
+ * {@link android.view.OnBackInvokedDispatcher}. The controller registers
+ * an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back
+ * navigation to launcher starts.
+ *
+ * Apps using the legacy back dispatching will keep triggering the WALLPAPER_OPEN remote
+ * transition registered in {@link QuickstepTransitionManager}.
+ *
+ */
+public class LauncherBackAnimationController {
+    private static final int CANCEL_TRANSITION_DURATION = 150;
+    private static final String TAG = "LauncherBackAnimationController";
+    private final DeviceProfile mDeviceProfile;
+    private final QuickstepTransitionManager mQuickstepTransitionManager;
+    private final Matrix mTransformMatrix = new Matrix();
+    private final RectF mTargetRectF = new RectF();
+    private final RectF mStartRectF = new RectF();
+    private final RectF mCurrentRect = new RectF();
+    private final BaseQuickstepLauncher mLauncher;
+    private final int mWindowScaleMarginX;
+    private final int mWindowScaleMarginY;
+    private final float mWindowScaleEndCornerRadius;
+    private final float mWindowScaleStartCornerRadius;
+
+    private RemoteAnimationTargetCompat mBackTarget;
+    private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    private boolean mSpringAnimationInProgress = false;
+    private boolean mAnimatorSetInProgress = false;
+    @BackEvent.SwipeEdge
+    private int mSwipeEdge;
+    private float mBackProgress = 0;
+    private boolean mBackInProgress = false;
+
+    public LauncherBackAnimationController(
+            DeviceProfile deviceProfile,
+            BaseQuickstepLauncher launcher,
+            QuickstepTransitionManager quickstepTransitionManager) {
+        mDeviceProfile = deviceProfile;
+        mLauncher = launcher;
+        mQuickstepTransitionManager = quickstepTransitionManager;
+        mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
+                mLauncher.getResources())
+                ? mLauncher.getResources().getDimensionPixelSize(
+                        R.dimen.swipe_back_window_corner_radius)
+                : 0;
+        mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+        mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.swipe_back_window_scale_x_margin);
+        mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.swipe_back_window_scale_y_margin);
+    }
+
+    /**
+     * Registers {@link IOnBackInvokedCallback} to receive back dispatches from shell.
+     * @param handler Handler to the thread to run the animations on.
+     */
+    public void registerBackCallbacks(Handler handler) {
+        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+        if (systemUiProxy == null) {
+            Log.e(TAG, "SystemUiProxy is null. Skip registering back invocation callbacks");
+            return;
+        }
+        systemUiProxy.setBackToLauncherCallback(
+                new IOnBackInvokedCallback.Stub() {
+                    @Override
+                    public void onBackCancelled() {
+                        handler.post(() -> resetPositionAnimated());
+                    }
+
+                    @Override
+                    public void onBackInvoked() {
+                        handler.post(() -> startTransition());
+                    }
+
+                    @Override
+                    public void onBackProgressed(BackEvent backEvent) {
+                        mBackProgress = backEvent.getProgress();
+                        // TODO: Update once the interpolation curve spec is finalized.
+                        mBackProgress =
+                                1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
+                                        - mBackProgress);
+                        if (!mBackInProgress) {
+                            startBack(backEvent);
+                        } else {
+                            updateBackProgress(mBackProgress);
+                        }
+                    }
+
+                    public void onBackStarted() { }
+                });
+    }
+
+    private void resetPositionAnimated() {
+        ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
+        cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
+        cancelAnimator.addUpdateListener(
+                animation -> {
+                    updateBackProgress((float) animation.getAnimatedValue());
+                });
+        cancelAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                finishAnimation();
+            }
+        });
+        cancelAnimator.start();
+    }
+
+    /** Unregisters the back to launcher callback in shell. */
+    public void unregisterBackCallbacks() {
+        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+        if (systemUiProxy != null) {
+            systemUiProxy.clearBackToLauncherCallback();
+        }
+    }
+
+    private void startBack(BackEvent backEvent) {
+        mBackInProgress = true;
+        RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
+
+        if (appTarget == null) {
+            return;
+        }
+
+        mTransaction.show(appTarget.leash).apply();
+        mTransaction.setAnimationTransaction();
+        mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+        mSwipeEdge = backEvent.getSwipeEdge();
+        float screenWidth = mDeviceProfile.widthPx;
+        float screenHeight = mDeviceProfile.heightPx;
+        float targetHeight = screenHeight - 2 * mWindowScaleMarginY;
+        float targetWidth = targetHeight * screenWidth / screenHeight;
+        float left;
+        if (mSwipeEdge == BackEvent.EDGE_LEFT) {
+            left = screenWidth - targetWidth - mWindowScaleMarginX;
+        } else {
+            left = mWindowScaleMarginX;
+        }
+        float top = mWindowScaleMarginY;
+        // TODO(b/218916755): Offset start rectangle in multiwindow mode.
+        mStartRectF.set(0, 0, screenWidth, screenHeight);
+        mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
+    }
+
+    private void updateBackProgress(float progress) {
+        if (mBackTarget == null) {
+            return;
+        }
+
+        mCurrentRect.set(
+                MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
+                MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
+                MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
+                MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
+        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
+                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
+
+        Rect currentRect = new Rect();
+        mCurrentRect.round(currentRect);
+
+        // Scale the target window to match the currentRectF.
+        final float scale = mCurrentRect.width() / mStartRectF.width();
+        mTransformMatrix.reset();
+        mTransformMatrix.setScale(scale, scale);
+        mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
+        Rect startRect = new Rect();
+        mStartRectF.round(startRect);
+        float cornerRadius = Utilities.mapRange(
+                progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
+        builder.withMatrix(mTransformMatrix)
+                .withWindowCrop(startRect)
+                .withCornerRadius(cornerRadius);
+        SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
+
+        if (surfaceParams.surface.isValid()) {
+            surfaceParams.applyTo(mTransaction);
+        }
+        mTransaction.apply();
+    }
+
+    private void startTransition() {
+        if (mBackTarget == null) {
+            // Trigger transition system instead of custom transition animation.
+            finishAnimation();
+            return;
+        }
+        if (mLauncher.isDestroyed()) {
+            return;
+        }
+        // TODO: Catch the moment when launcher becomes visible after the top app un-occludes
+        //  launcher and start animating afterwards. Currently we occasionally get a flicker from
+        //  animating when launcher is still invisible.
+        if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
+            mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
+            mLauncher.getStateManager().moveToRestState();
+        }
+
+        Pair<RectFSpringAnim, AnimatorSet> pair =
+                mQuickstepTransitionManager.createWallpaperOpenAnimations(
+                    new RemoteAnimationTargetCompat[]{mBackTarget},
+                    new RemoteAnimationTargetCompat[]{},
+                    false /* fromUnlock */,
+                    mCurrentRect);
+        startTransitionAnimations(pair.first, pair.second);
+        mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
+    }
+
+    private void finishAnimation() {
+        mBackTarget = null;
+        mBackInProgress = false;
+        mBackProgress = 0;
+        mSwipeEdge = BackEvent.EDGE_LEFT;
+        mTransformMatrix.reset();
+        mTargetRectF.setEmpty();
+        mCurrentRect.setEmpty();
+        mStartRectF.setEmpty();
+        mAnimatorSetInProgress = false;
+        mSpringAnimationInProgress = false;
+        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+        if (systemUiProxy != null) {
+            SystemUiProxy.INSTANCE.getNoCreate().onBackToLauncherAnimationFinished();
+        }
+    }
+
+    private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
+        mAnimatorSetInProgress = anim != null;
+        mSpringAnimationInProgress = springAnim != null;
+        if (springAnim != null) {
+            springAnim.addAnimatorListener(
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mSpringAnimationInProgress = false;
+                            tryFinishBackAnimation();
+                        }
+                    }
+            );
+        }
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimatorSetInProgress = false;
+                tryFinishBackAnimation();
+            }
+        });
+        anim.start();
+    }
+
+    private void tryFinishBackAnimation() {
+        if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) {
+            finishAnimation();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index af6cb84..50d1244 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -233,11 +233,13 @@
 
         return mActivity.getFirstMatchForAppClose(launchCookieItemId,
                 runningTaskView.getTask().key.getComponent().getPackageName(),
-                UserHandle.of(runningTaskView.getTask().key.userId));
+                UserHandle.of(runningTaskView.getTask().key.userId),
+                false /* supportsAllAppsState */);
     }
 
     @Override
     protected void finishRecentsControllerToHome(Runnable callback) {
+        mRecentsView.cleanupRemoteTargets();
         mRecentsAnimationController.finish(
                 true /* toRecents */, callback, true /* sendUserLeaveHint */);
     }
@@ -283,10 +285,5 @@
                     getViewIgnoredInWorkspaceRevealAnimation())
                     .start();
         }
-
-        @Override
-        public boolean supportSwipePipToHome() {
-            return true;
-        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 4d13253..895cf89 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -34,11 +34,12 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.DisplayController.NavigationMode;
+import com.android.launcher3.util.window.CachedDisplayInfo;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Objects;
 
 /**
  * Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
@@ -50,55 +51,17 @@
  */
 class OrientationTouchTransformer {
 
-    private static class CurrentDisplay {
-        public Point size;
-        public int rotation;
-
-        CurrentDisplay() {
-            this.size = new Point(0, 0);
-            this.rotation = 0;
-        }
-
-        CurrentDisplay(Point size, int rotation) {
-            this.size = size;
-            this.rotation = rotation;
-        }
-
-        @Override
-        public String toString() {
-            return "CurrentDisplay:"
-                    + " rotation: " + rotation
-                    + " size: " + size;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            CurrentDisplay display = (CurrentDisplay) o;
-            if (rotation != display.rotation) return false;
-
-            return Objects.equals(size, display.size);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(size, rotation);
-        }
-    };
-
     private static final String TAG = "OrientationTouchTransformer";
     private static final boolean DEBUG = false;
 
     private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
 
-    private final Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
-            new HashMap<CurrentDisplay, OrientationRectF>();
+    private final Map<CachedDisplayInfo, OrientationRectF> mSwipeTouchRegions =
+            new HashMap<CachedDisplayInfo, OrientationRectF>();
     private final RectF mAssistantLeftRegion = new RectF();
     private final RectF mAssistantRightRegion = new RectF();
     private final RectF mOneHandedModeRegion = new RectF();
-    private CurrentDisplay mCurrentDisplay = new CurrentDisplay();
+    private CachedDisplayInfo mCachedDisplayInfo = new CachedDisplayInfo();
     private int mNavBarGesturalHeight;
     private final int mNavBarLargerGesturalHeight;
     private boolean mEnableMultipleRegions;
@@ -112,7 +75,7 @@
      * mQuickstepStartingRotation only updates when device rotation matches touch rotation.
      */
     private int mActiveTouchRotation;
-    private SysUINavigationMode.Mode mMode;
+    private NavigationMode mMode;
     private QuickStepContractInfo mContractInfo;
 
     /**
@@ -135,7 +98,7 @@
     }
 
 
-    OrientationTouchTransformer(Resources resources, SysUINavigationMode.Mode mode,
+    OrientationTouchTransformer(Resources resources, NavigationMode mode,
             QuickStepContractInfo contractInfo) {
         mResources = resources;
         mMode = mode;
@@ -155,7 +118,7 @@
         resetSwipeRegions(info);
     }
 
-    void setNavigationMode(SysUINavigationMode.Mode newMode, Info info, Resources newRes) {
+    void setNavigationMode(NavigationMode newMode, Info info, Resources newRes) {
         if (DEBUG) {
             Log.d(TAG, "setNavigationMode new: " + newMode + " oldMode: " + mMode + " " + this);
         }
@@ -183,22 +146,22 @@
      * @see #enableMultipleRegions(boolean, Info)
      */
     void createOrAddTouchRegion(Info info) {
-        mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation);
+        mCachedDisplayInfo = new CachedDisplayInfo(info.currentSize, info.rotation);
 
         if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
-                && mCurrentDisplay.rotation == mQuickStepStartingRotation) {
+                && mCachedDisplayInfo.rotation == mQuickStepStartingRotation) {
             // User already was swiping and the current screen is same rotation as the starting one
             // Remove active nav bars in other rotations except for the one we started out in
             resetSwipeRegions(info);
             return;
         }
-        OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplay);
+        OrientationRectF region = mSwipeTouchRegions.get(mCachedDisplayInfo);
         if (region != null) {
             return;
         }
 
         if (mEnableMultipleRegions) {
-            mSwipeTouchRegions.put(mCurrentDisplay, createRegionForDisplay(info));
+            mSwipeTouchRegions.put(mCachedDisplayInfo, createRegionForDisplay(info));
         } else {
             resetSwipeRegions(info);
         }
@@ -212,8 +175,7 @@
      * @param info The current displayInfo which will be the start of the quickswitch gesture
      */
     void enableMultipleRegions(boolean enableMultipleRegions, Info info) {
-        mEnableMultipleRegions = enableMultipleRegions &&
-                mMode != SysUINavigationMode.Mode.TWO_BUTTONS;
+        mEnableMultipleRegions = enableMultipleRegions && mMode != NavigationMode.TWO_BUTTONS;
         if (mEnableMultipleRegions) {
             mQuickStepStartingRotation = info.rotation;
         } else {
@@ -245,40 +207,39 @@
      */
     private void resetSwipeRegions(Info region) {
         if (DEBUG) {
-            Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation);
+            Log.d(TAG, "clearing all regions except rotation: " + mCachedDisplayInfo.rotation);
         }
 
-        mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation);
-        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
+        mCachedDisplayInfo = new CachedDisplayInfo(region.currentSize, region.rotation);
+        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
         if (regionToKeep == null) {
             regionToKeep = createRegionForDisplay(region);
         }
         mSwipeTouchRegions.clear();
-        mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
+        mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
         updateAssistantRegions(regionToKeep);
     }
 
     private void resetSwipeRegions() {
-        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
+        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
         mSwipeTouchRegions.clear();
         if (regionToKeep != null) {
-            mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
+            mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
             updateAssistantRegions(regionToKeep);
         }
     }
 
     private OrientationRectF createRegionForDisplay(Info display) {
         if (DEBUG) {
-            Log.d(TAG, "creating rotation region for: " + mCurrentDisplay.rotation
+            Log.d(TAG, "creating rotation region for: " + mCachedDisplayInfo.rotation
             + " with mode: " + mMode + " displayRotation: " + display.rotation);
         }
 
         Point size = display.currentSize;
         int rotation = display.rotation;
         int touchHeight = mNavBarGesturalHeight;
-        OrientationRectF orientationRectF =
-                new OrientationRectF(0, 0, size.x, size.y, rotation);
-        if (mMode == SysUINavigationMode.Mode.NO_BUTTON) {
+        OrientationRectF orientationRectF = new OrientationRectF(0, 0, size.x, size.y, rotation);
+        if (mMode == NavigationMode.NO_BUTTON) {
             orientationRectF.top = orientationRectF.bottom - touchHeight;
             updateAssistantRegions(orientationRectF);
         } else {
@@ -369,7 +330,7 @@
                                 true);
                     }
                 } else {
-                    mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
+                    mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
                             true);
                 }
                 break;
@@ -388,7 +349,7 @@
                                 true);
                     }
                 } else {
-                    mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
+                    mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
                             true);
                 }
                 mLastRectTouched = null;
@@ -404,11 +365,12 @@
                     if (rect == null) {
                         continue;
                     }
-                    if (rect.applyTransformFromRotation(event, mCurrentDisplay.rotation, false)) {
+                    if (rect.applyTransformFromRotation(
+                            event, mCachedDisplayInfo.rotation, false)) {
                         mLastRectTouched = rect;
                         mActiveTouchRotation = rect.getRotation();
                         if (mEnableMultipleRegions
-                                && mCurrentDisplay.rotation == mActiveTouchRotation) {
+                                && mCachedDisplayInfo.rotation == mActiveTouchRotation) {
                             // TODO(b/154580671) might make this block unnecessary
                             // Start a touch session for the default nav region for the display
                             mQuickStepStartingRotation = mLastRectTouched.getRotation();
@@ -431,7 +393,7 @@
         pw.println("  lastTouchedRegion=" + mLastRectTouched);
         pw.println("  multipleRegionsEnabled=" + mEnableMultipleRegions);
         StringBuilder regions = new StringBuilder("  currentTouchableRotations=");
-        for (CurrentDisplay key: mSwipeTouchRegions.keySet()) {
+        for (CachedDisplayInfo key: mSwipeTouchRegions.keySet()) {
             OrientationRectF rectF = mSwipeTouchRegions.get(key);
             regions.append(rectF).append(" ");
         }
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 17baa3a..a7bdcc6 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -54,6 +54,12 @@
     public static final int TYPE_TOGGLE = 4;
     public static final int TYPE_HOME = 5;
 
+    /**
+     * Use case for needing a queue is double tapping recents button in 3 button nav.
+     * Size of 2 should be enough. We'll toss in one more because we're kind hearted.
+     */
+    private final static int MAX_QUEUE_SIZE = 3;
+
     private static final String TRANSITION_NAME = "Transition:toOverview";
 
     private final TouchInteractionService mService;
@@ -105,10 +111,15 @@
     }
 
     /**
-     * Adds a command to be executed next, after all pending tasks are completed
+     * Adds a command to be executed next, after all pending tasks are completed.
+     * Max commands that can be queued is {@link #MAX_QUEUE_SIZE}.
+     * Requests after reaching that limit will be silently dropped.
      */
     @BinderThread
     public void addCommand(int type) {
+        if (mPendingCommands.size() > MAX_QUEUE_SIZE) {
+            return;
+        }
         CommandInfo cmd = new CommandInfo(type);
         MAIN_EXECUTOR.execute(() -> addCommand(cmd));
     }
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 678372c..5521020 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -23,11 +23,11 @@
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Log;
+import android.view.ThreadedRenderer;
 
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.MainProcessInitializer;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.ThreadedRendererCompat;
 
 @SuppressWarnings("unused")
 @TargetApi(Build.VERSION_CODES.R)
@@ -60,8 +60,8 @@
         super.init(context);
 
         // Elevate GPU priority for Quickstep and Remote animations.
-        ThreadedRendererCompat.setContextPriority(
-                ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+        ThreadedRenderer.setContextPriority(
+                ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
 
         // Enable binder tracing on system server for calls originating from Launcher
         try {
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index db92e33..4f0b976 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -24,6 +24,7 @@
 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.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -38,6 +39,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
 import android.view.Display;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
@@ -135,8 +137,8 @@
         SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
 
         SplitSelectStateController controller =
-                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this),
-                        getStateManager(), null /*depthController*/);
+                new SplitSelectStateController(this, mHandler, getStateManager(),
+                        null /* depthController */);
         mDragLayer.recreateControllers();
         mFallbackRecentsView.init(mActionsView, controller);
 
@@ -312,6 +314,7 @@
     protected void onStart() {
         // Set the alpha to 1 before calling super, as it may get set back to 0 due to
         // onActivityStart callback.
+        Log.d(BAD_STATE, "RecentsActivity onStart mFallbackRecentsView.setContentAlpha(1)");
         mFallbackRecentsView.setContentAlpha(1);
         super.onStart();
         mFallbackRecentsView.updateLocusId();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index f343485..c120b32 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -17,9 +17,13 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 
+import android.os.RemoteException;
+import android.util.Log;
 import android.view.IRecentsAnimationController;
 import android.view.SurfaceControl;
+import android.view.WindowManagerGlobal;
 import android.window.PictureInPictureSurfaceTransaction;
 
 import androidx.annotation.NonNull;
@@ -39,6 +43,7 @@
  */
 public class RecentsAnimationController {
 
+    private static final String TAG = "RecentsAnimationController";
     private final RecentsAnimationControllerCompat mController;
     private final Consumer<RecentsAnimationController> mOnFinishedListener;
     private final boolean mAllowMinimizeSplitScreen;
@@ -74,7 +79,16 @@
         if (mUseLauncherSysBarFlags != useLauncherSysBarFlags) {
             mUseLauncherSysBarFlags = useLauncherSysBarFlags;
             UI_HELPER_EXECUTOR.execute(() -> {
-                mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
+                if (!ENABLE_SHELL_TRANSITIONS) {
+                    mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
+                } else {
+                    try {
+                        WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars(
+                                useLauncherSysBarFlags);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to reach window manager", e);
+                    }
+                }
             });
         }
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index ad57683..f1ace49 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -21,12 +21,13 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
+import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -71,9 +72,8 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.SettingsCache;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -89,15 +89,11 @@
 /**
  * Manages the state of the system during a swipe up gesture.
  */
-public class RecentsAnimationDeviceState implements
-        NavigationModeChangeListener,
-        DisplayInfoChangeListener,
-        OneHandedModeChangeListener {
+public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
 
     static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     private final Context mContext;
-    private final SysUINavigationMode mSysUiNavMode;
     private final DisplayController mDisplayController;
     private final int mDisplayId;
     private final RotationTouchHelper mRotationTouchHelper;
@@ -110,7 +106,7 @@
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
 
     private @SystemUiStateFlags int mSystemUiStateFlags;
-    private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
+    private NavigationMode mMode = THREE_BUTTONS;
     private NavBarPosition mNavBarPosition;
 
     private final Region mDeferredGestureRegion = new Region();
@@ -148,10 +144,8 @@
     public RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches) {
         mContext = context;
         mDisplayController = DisplayController.INSTANCE.get(context);
-        mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
         mDisplayId = DEFAULT_DISPLAY;
         mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
-        runOnDestroy(() -> mDisplayController.removeChangeListener(this));
         mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
         if (isInstanceForTouches) {
             // rotationTouchHelper doesn't get initialized after being destroyed, so only destroy
@@ -180,9 +174,10 @@
         };
         runOnDestroy(mExclusionListener::unregister);
 
-        // Register for navigation mode changes
-        onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
-        runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
+        // Register for display changes changes
+        mDisplayController.addChangeListener(this);
+        onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL);
+        runOnDestroy(() -> mDisplayController.removeChangeListener(this));
 
         // Add any blocked activities
         String[] blockingActivities;
@@ -267,47 +262,36 @@
      * Adds a listener for the nav mode change, guaranteed to be called after the device state's
      * mode has changed.
      */
-    public void addNavigationModeChangedCallback(NavigationModeChangeListener listener) {
-        listener.onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(listener));
-        runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
-    }
-
-    @Override
-    public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
-        mDisplayController.removeChangeListener(this);
-        mDisplayController.addChangeListener(this);
-        onDisplayInfoChanged(mContext, mDisplayController.getInfo(), CHANGE_ALL);
-
-        if (newMode == NO_BUTTON) {
-            mExclusionListener.register();
-        } else {
-            mExclusionListener.unregister();
-        }
-
-        mNavBarPosition = new NavBarPosition(newMode, mDisplayController.getInfo());
-        mMode = newMode;
+    public void addNavigationModeChangedCallback(Runnable callback) {
+        DisplayController.DisplayInfoChangeListener listener = (context, info, flags) -> {
+            if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+                callback.run();
+            }
+        };
+        mDisplayController.addChangeListener(listener);
+        callback.run();
+        runOnDestroy(() -> mDisplayController.removeChangeListener(listener));
     }
 
     @Override
     public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if ((flags & CHANGE_ROTATION) != 0) {
+        if ((flags & (CHANGE_ROTATION | CHANGE_NAVIGATION_MODE)) != 0) {
+            mMode = info.navigationMode;
             mNavBarPosition = new NavBarPosition(mMode, info);
+
+            if (mMode == NO_BUTTON) {
+                mExclusionListener.register();
+            } else {
+                mExclusionListener.unregister();
+            }
         }
     }
 
-    @Override
     public void onOneHandedModeChanged(int newGesturalHeight) {
         mRotationTouchHelper.setGesturalHeight(newGesturalHeight);
     }
 
     /**
-     * @return the current navigation mode for the device.
-     */
-    public SysUINavigationMode.Mode getNavMode() {
-        return mMode;
-    }
-
-    /**
      * @return the nav bar position for the current nav bar mode and display rotation.
      */
     public NavBarPosition getNavBarPosition() {
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 35efddf..dad5071 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -20,9 +20,10 @@
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -33,6 +34,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -42,22 +44,22 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-public class RotationTouchHelper implements
-        SysUINavigationMode.NavigationModeChangeListener,
-        DisplayInfoChangeListener {
+/**
+ * Helper class for transforming touch events
+ */
+public class RotationTouchHelper implements DisplayInfoChangeListener {
 
     public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
             new MainThreadInitializedObject<>(RotationTouchHelper::new);
 
     private OrientationTouchTransformer mOrientationTouchTransformer;
     private DisplayController mDisplayController;
-    private SysUINavigationMode mSysUiNavMode;
     private int mDisplayId;
     private int mDisplayRotation;
 
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
 
-    private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
+    private NavigationMode mMode = THREE_BUTTONS;
 
     private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
         @Override
@@ -144,16 +146,16 @@
         }
         mDisplayController = DisplayController.INSTANCE.get(mContext);
         Resources resources = mContext.getResources();
-        mSysUiNavMode = SysUINavigationMode.INSTANCE.get(mContext);
         mDisplayId = DEFAULT_DISPLAY;
 
         mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
                 () -> QuickStepContract.getWindowCornerRadius(mContext));
 
         // Register for navigation mode changes
-        SysUINavigationMode.Mode newMode = mSysUiNavMode.addModeChangeListener(this);
-        onNavModeChangedInternal(newMode, newMode.hasGestures);
-        runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
+        mDisplayController.addChangeListener(this);
+        DisplayController.Info info = mDisplayController.getInfo();
+        onDisplayInfoChangedInternal(info, CHANGE_ALL, info.navigationMode.hasGestures);
+        runOnDestroy(() -> mDisplayController.removeChangeListener(this));
 
         mOrientationListener = new OrientationEventListener(mContext) {
             @Override
@@ -242,66 +244,56 @@
                 event.getY(pointerIndex));
     }
 
-
     @Override
-    public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
-        onNavModeChangedInternal(newMode, false);
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        onDisplayInfoChangedInternal(info, flags, false);
     }
 
-    /**
-     * @param forceRegister if {@code true}, this will register {@link #mFrozenTaskListener} via
-     *                      {@link #setupOrientationSwipeHandler()}
-     */
-    private void onNavModeChangedInternal(SysUINavigationMode.Mode newMode, boolean forceRegister) {
-        mDisplayController.removeChangeListener(this);
-        mDisplayController.addChangeListener(this);
-        onDisplayInfoChanged(mContext, mDisplayController.getInfo(), CHANGE_ALL);
+    private void onDisplayInfoChangedInternal(Info info, int flags, boolean forceRegister) {
+        if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN | CHANGE_NAVIGATION_MODE)) != 0) {
+            mDisplayRotation = info.rotation;
 
-        mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
-                mContext.getResources());
+            if (mMode.hasGestures) {
+                updateGestureTouchRegions();
+                mOrientationTouchTransformer.createOrAddTouchRegion(info);
+                mCurrentAppRotation = mDisplayRotation;
 
-        if (forceRegister || (!mMode.hasGestures && newMode.hasGestures)) {
-            setupOrientationSwipeHandler();
-        } else if (mMode.hasGestures && !newMode.hasGestures){
-            destroyOrientationSwipeHandlerCallback();
+                /* Update nav bars on the following:
+                 * a) if this is coming from an activity rotation OR
+                 *   aa) we launch an app in the orientation that user is already in
+                 * b) We're not in overview, since overview will always be portrait (w/o home
+                 *   rotation)
+                 * c) We're actively in quickswitch mode
+                 */
+                if ((mPrioritizeDeviceRotation
+                        || mCurrentAppRotation == mSensorRotation)
+                        // switch to an app of orientation user is in
+                        && !mInOverview
+                        && mTaskListFrozen) {
+                    toggleSecondaryNavBarsForRotation();
+                }
+            }
         }
 
-        mMode = newMode;
+        if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+            NavigationMode newMode = info.navigationMode;
+            mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
+                    mContext.getResources());
+
+            if (forceRegister || (!mMode.hasGestures && newMode.hasGestures)) {
+                setupOrientationSwipeHandler();
+            } else if (mMode.hasGestures && !newMode.hasGestures) {
+                destroyOrientationSwipeHandlerCallback();
+            }
+
+            mMode = newMode;
+        }
     }
 
     public int getDisplayRotation() {
         return mDisplayRotation;
     }
 
-    @Override
-    public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) {
-            return;
-        }
-
-        mDisplayRotation = info.rotation;
-
-        if (!mMode.hasGestures) {
-            return;
-        }
-        updateGestureTouchRegions();
-        mOrientationTouchTransformer.createOrAddTouchRegion(info);
-        mCurrentAppRotation = mDisplayRotation;
-
-        /* Update nav bars on the following:
-         * a) if this is coming from an activity rotation OR
-         *   aa) we launch an app in the orientation that user is already in
-         * b) We're not in overview, since overview will always be portrait (w/o home rotation)
-         * c) We're actively in quickswitch mode
-         */
-        if ((mPrioritizeDeviceRotation
-                || mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
-                && !mInOverview
-                && mTaskListFrozen) {
-            toggleSecondaryNavBarsForRotation();
-        }
-    }
-
     /**
      * Sets the gestural height.
      */
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 8d9a685..8862073 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -184,14 +184,6 @@
         public void onCancel() { }
 
         /**
-         * @return {@code true} if this factory supports animating an Activity to PiP window on
-         * swiping up to home.
-         */
-        public boolean supportSwipePipToHome() {
-            return false;
-        }
-
-        /**
          * @param progress The progress of the animation to the home screen.
          * @return The current alpha to set on the animating app window.
          */
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
deleted file mode 100644
index 406414c..0000000
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
-import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.util.MainThreadInitializedObject;
-
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Observer for the resource config that specifies the navigation bar mode.
- */
-public class SysUINavigationMode {
-
-    public enum Mode {
-        THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
-        TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
-        NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
-
-        public final boolean hasGestures;
-        public final int resValue;
-        public final LauncherEvent launcherEvent;
-
-        Mode(boolean hasGestures, int resValue, LauncherEvent launcherEvent) {
-            this.hasGestures = hasGestures;
-            this.resValue = resValue;
-            this.launcherEvent = launcherEvent;
-        }
-    }
-
-    public static Mode getMode(Context context) {
-        return INSTANCE.get(context).getMode();
-    }
-
-    public static boolean getImeDrawsImeNavBar(Context context) {
-        return INSTANCE.get(context).getImeDrawsImeNavBar();
-    }
-
-    public static final MainThreadInitializedObject<SysUINavigationMode> INSTANCE =
-            new MainThreadInitializedObject<>(SysUINavigationMode::new);
-
-    private static final String TAG = "SysUINavigationMode";
-    private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-    private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
-    private static final String IME_DRAWS_IME_NAV_BAR_RES_NAME = "config_imeDrawsImeNavBar";
-    private static final String TARGET_OVERLAY_PACKAGE = "android";
-
-    private final Context mContext;
-    private Mode mMode;
-    private boolean mImeDrawsImeNavBar;
-
-    private int mNavBarGesturalHeight;
-    private int mNavBarLargerGesturalHeight;
-
-    private final List<NavigationModeChangeListener> mChangeListeners =
-            new CopyOnWriteArrayList<>();
-
-    public SysUINavigationMode(Context context) {
-        mContext = context;
-        initializeMode();
-
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                updateMode();
-                updateGesturalHeight();
-            }
-        }, getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
-    }
-
-    /** Updates navigation mode when needed. */
-    public void updateMode() {
-        Mode oldMode = mMode;
-        initializeMode();
-        if (mMode != oldMode) {
-            dispatchModeChange();
-        }
-    }
-
-    private void updateGesturalHeight() {
-        int newGesturalHeight = ResourceUtils.getDimenByName(
-                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources(),
-                INVALID_RESOURCE_HANDLE);
-
-        if (newGesturalHeight == INVALID_RESOURCE_HANDLE) {
-            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
-            return;
-        }
-
-        if (mNavBarGesturalHeight != newGesturalHeight) {
-            mNavBarGesturalHeight = newGesturalHeight;
-        }
-
-        int newLargerGesturalHeight = ResourceUtils.getDimenByName(
-                ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, mContext.getResources(),
-                INVALID_RESOURCE_HANDLE);
-        if (newLargerGesturalHeight == INVALID_RESOURCE_HANDLE) {
-            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
-            return;
-        }
-        if (mNavBarLargerGesturalHeight != newLargerGesturalHeight) {
-            mNavBarLargerGesturalHeight = newLargerGesturalHeight;
-        }
-    }
-
-    private void initializeMode() {
-        int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
-                mContext.getResources(), INVALID_RESOURCE_HANDLE);
-        mNavBarGesturalHeight = ResourceUtils.getDimenByName(
-                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources(),
-                INVALID_RESOURCE_HANDLE);
-        mNavBarLargerGesturalHeight = ResourceUtils.getDimenByName(
-                ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, mContext.getResources(),
-                mNavBarGesturalHeight);
-        mImeDrawsImeNavBar = ResourceUtils.getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME,
-                mContext.getResources(), false);
-
-        if (modeInt == INVALID_RESOURCE_HANDLE) {
-            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
-            return;
-        }
-
-        for (Mode m : Mode.values()) {
-            if (m.resValue == modeInt) {
-                mMode = m;
-            }
-        }
-    }
-
-    private void dispatchModeChange() {
-        for (NavigationModeChangeListener listener : mChangeListeners) {
-            listener.onNavigationModeChanged(mMode);
-        }
-    }
-
-    public Mode addModeChangeListener(NavigationModeChangeListener listener) {
-        mChangeListeners.add(listener);
-        return mMode;
-    }
-
-    public void removeModeChangeListener(NavigationModeChangeListener listener) {
-        mChangeListeners.remove(listener);
-    }
-
-    public Mode getMode() {
-        return mMode;
-    }
-
-    public boolean getImeDrawsImeNavBar() {
-        return mImeDrawsImeNavBar;
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println("SysUINavigationMode:");
-        pw.println("  mode=" + mMode.name());
-        pw.println("  mImeDrawsImeNavBar=:" + mImeDrawsImeNavBar);
-        pw.println("  mNavBarGesturalHeight=:" + mNavBarGesturalHeight);
-    }
-
-    public interface NavigationModeChangeListener {
-        void onNavigationModeChanged(Mode newMode);
-    }
-
-    public interface OneHandedModeChangeListener {
-        void onOneHandedModeChanged(int newGesturalHeight);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index d28796c..5ef89d3 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,6 +17,7 @@
 
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.app.PendingIntent;
@@ -38,7 +39,10 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
+import android.window.IOnBackInvokedCallback;
 
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -47,6 +51,7 @@
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
 import com.android.systemui.shared.system.smartspace.SmartspaceState;
+import com.android.wm.shell.back.IBackAnimation;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.pip.IPipAnimationListener;
@@ -65,8 +70,7 @@
 /**
  * Holds the reference to SystemUI.
  */
-public class SystemUiProxy implements ISystemUiProxy,
-        SysUINavigationMode.NavigationModeChangeListener {
+public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayInfoChangeListener {
     private static final String TAG = SystemUiProxy.class.getSimpleName();
 
     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
@@ -80,6 +84,7 @@
     private IShellTransitions mShellTransitions;
     private IStartingWindow mStartingWindow;
     private IRecentTasks mRecentTasks;
+    private IBackAnimation mBackAnimation;
     private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
         MAIN_EXECUTOR.execute(() -> clearProxy());
     };
@@ -94,6 +99,7 @@
     private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
     private IRecentTasksListener mRecentTasksListener;
     private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
+    private IOnBackInvokedCallback mBackToLauncherCallback;
 
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
@@ -107,13 +113,15 @@
     private int mLastSystemUiStateFlags;
 
     public SystemUiProxy(Context context) {
-        SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+        DisplayController.INSTANCE.get(context).addChangeListener(this);
     }
 
     @Override
-    public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
-        // Whenever the nav mode changes, force reset the nav button alpha
-        setNavBarButtonAlpha(1f, false);
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+            // Whenever the nav mode changes, force reset the nav button alpha
+            setNavBarButtonAlpha(1f, false);
+        }
     }
 
     @Override
@@ -158,8 +166,8 @@
     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
             IOneHanded oneHanded, IShellTransitions shellTransitions,
             IStartingWindow startingWindow, IRecentTasks recentTasks,
-            ISysuiUnlockAnimationController sysuiUnlockAnimationController) {
-
+            ISysuiUnlockAnimationController sysuiUnlockAnimationController,
+            IBackAnimation backAnimation) {
         unlinkToDeath();
         mSystemUiProxy = proxy;
         mPip = pip;
@@ -169,6 +177,7 @@
         mStartingWindow = startingWindow;
         mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
         mRecentTasks = recentTasks;
+        mBackAnimation = backAnimation;
         linkToDeath();
         // re-attach the listeners once missing due to setProxy has not been initialized yet.
         if (mPipAnimationListener != null && mPip != null) {
@@ -191,6 +200,9 @@
         if (mRecentTasksListener != null && mRecentTasks != null) {
             registerRecentTasksListener(mRecentTasksListener);
         }
+        if (mBackAnimation != null && mBackToLauncherCallback != null) {
+            setBackToLauncherCallback(mBackToLauncherCallback);
+        }
 
         if (mPendingSetNavButtonAlpha != null) {
             mPendingSetNavButtonAlpha.run();
@@ -199,7 +211,7 @@
     }
 
     public void clearProxy() {
-        setProxy(null, null, null, null, null, null, null, null);
+        setProxy(null, null, null, null, null, null, null, null, null);
     }
 
     // TODO(141886704): Find a way to remove this
@@ -541,11 +553,16 @@
         return null;
     }
 
-    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+    /**
+     * Notifies WM Shell that launcher has finished all the animation for swipe to home. WM Shell
+     * can choose to fade out the overlay when entering PIP is finished, and WM Shell should be
+     * responsible for cleaning up the overlay.
+     */
+    public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
             SurfaceControl overlay) {
         if (mPip != null) {
             try {
-                mPip.stopSwipePipToHome(componentName, destinationBounds, overlay);
+                mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call stopSwipePipToHome");
             }
@@ -813,11 +830,59 @@
         mRecentTasksListener = null;
     }
 
+    //
+    // Back navigation transitions
+    //
+
+    /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
+    public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+        mBackToLauncherCallback = callback;
+        if (mBackAnimation == null) {
+            return;
+        }
+        try {
+            mBackAnimation.setBackToLauncherCallback(callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed call setBackToLauncherCallback", e);
+        }
+    }
+
+    /** Clears the previously registered {@link IOnBackInvokedCallback}. */
+    public void clearBackToLauncherCallback() {
+        mBackToLauncherCallback = null;
+        if (mBackAnimation == null) {
+            return;
+        }
+        try {
+            mBackAnimation.clearBackToLauncherCallback();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
+        }
+    }
+
+    /**
+     * Notifies shell that all back to launcher animations have finished (including the transition
+     * that plays after the gesture is committed and before the app is closed.
+     */
+    public void onBackToLauncherAnimationFinished() {
+        if (mBackAnimation != null) {
+            try {
+                mBackAnimation.onBackToLauncherAnimationFinished();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call onBackAnimationFinished", e);
+            }
+        }
+    }
+
     public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
         if (mRecentTasks != null) {
             try {
-                return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
-                        RECENT_IGNORE_UNAVAILABLE, userId)));
+                final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks,
+                        RECENT_IGNORE_UNAVAILABLE, userId);
+                if (rawTasks == null) {
+                    return new ArrayList<>();
+                }
+                return new ArrayList<>(Arrays.asList(rawTasks));
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call getRecentTasks", e);
             }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 5e298f4..57b42f8 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -44,14 +44,14 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.HashMap;
 
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
     public static final boolean ENABLE_SHELL_TRANSITIONS =
-            SystemProperties.getBoolean("persist.debug.shell_transit", false);
+            SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
     public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
-            && SystemProperties.getBoolean("persist.debug.shell_transit_rotate", false);
+            && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
 
     private RecentsAnimationController mController;
     private RecentsAnimationCallbacks mCallbacks;
@@ -156,12 +156,26 @@
                 RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
                 BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
                 // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
-                RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appearedTaskTargets)
-                        .filter(remoteAnimationTarget ->
-                                remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
+                final ArrayList<RemoteAnimationTargetCompat> tmpNonHomeApps = new ArrayList<>();
+                final ArrayList<RemoteAnimationTargetCompat> tmpHomeApps = new ArrayList<>();
+                for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
+                    if (compat.activityType != ACTIVITY_TYPE_HOME) {
+                        tmpNonHomeApps.add(compat);
+                    } else {
+                        tmpHomeApps.add(compat);
+                    }
+                }
+                RemoteAnimationTarget[] nonHomeApps = tmpNonHomeApps.stream()
                         .map(RemoteAnimationTargetCompat::unwrap)
                         .toArray(RemoteAnimationTarget[]::new);
-
+                RemoteAnimationTarget[] homeApps = tmpHomeApps.stream()
+                        .map(RemoteAnimationTargetCompat::unwrap)
+                        .toArray(RemoteAnimationTarget[]::new);
+                if (homeApps.length > 0
+                        && activityInterface.getCreatedActivity() instanceof RecentsActivity) {
+                    ((RecentsActivity) activityInterface.getCreatedActivity()).startHome();
+                    return;
+                }
                 RemoteAnimationTarget[] nonAppTargets =
                         SystemUiProxy.INSTANCE.getNoCreate()
                                 .onGoingToRecentsLegacy(false, nonHomeApps);
@@ -198,8 +212,16 @@
             RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
                     mController != null ? mController.getController() : null,
                     mCtx.getIApplicationThread());
-            final ActivityOptions options = ActivityOptionsCompat.makeRemoteTransition(transition)
-                    .setTransientLaunch();
+            final ActivityOptions options = ActivityOptionsCompat.makeRemoteTransition(transition);
+            // Allowing to pause Home if Home is top activity and Recents is not Home. So when user
+            // start home when recents animation is playing, the home activity can be resumed again
+            // to let the transition controller collect Home activity.
+            ActivityManager.RunningTaskInfo rti = gestureState.getRunningTask();
+            boolean homeIsOnTop = rti != null && rti.topActivity != null
+                    && rti.topActivity.equals(gestureState.getHomeIntent().getComponent());
+            if (!homeIsOnTop) {
+                options.setTransientLaunch();
+            }
             options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
             UI_HELPER_EXECUTOR.execute(() -> mCtx.startActivity(intent, options.toBundle()));
         } else {
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 6c71da9..300f085 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 
+import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -48,7 +49,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.TaskDescriptionCompat;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -154,7 +154,7 @@
 
         // Load icon
         // TODO: Load icon resource (b/143363444)
-        Bitmap icon = TaskDescriptionCompat.getIcon(desc, key.userId);
+        Bitmap icon = getIcon(desc, key.userId);
         if (icon != null) {
             entry.icon = getBitmapInfo(
                     new BitmapDrawable(mContext.getResources(), icon),
@@ -193,6 +193,14 @@
         return entry;
     }
 
+    private Bitmap getIcon(ActivityManager.TaskDescription desc, int userId) {
+        if (desc.getInMemoryIcon() != null) {
+            return desc.getInMemoryIcon();
+        }
+        return ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+                desc.getIconFilename(), userId);
+    }
+
     private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
         PackageManager pm = mContext.getPackageManager();
         String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index ecb30e5..e731b79 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -258,9 +258,8 @@
 
         @Override
         protected ActivityOptions makeLaunchOptions(Activity activity) {
-            final ActivityCompat act = new ActivityCompat(activity);
             final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
-                    act.getDisplayId());
+                    activity.getDisplayId());
             if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
                 return null;
             }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index e67b0a5..516ecd4 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -37,6 +37,7 @@
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -55,6 +56,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.util.Log;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.window.TransitionInfo;
@@ -577,6 +579,29 @@
             launcherAnim = dp.isTablet
                     ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
                     : recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
+            if (dp.isTablet) {
+                Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator alpha=" + 0);
+                launcherAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onStart");
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        float alpha = recentsView == null
+                                ? -1
+                                : RecentsView.CONTENT_ALPHA.get(recentsView);
+                        Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onCancel, alpha="
+                                + alpha);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        Log.d(BAD_STATE, "TVU composeRecentsLaunchAnimator onEnd");
+                    }
+                });
+            }
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
             launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 2ed75b5..ff67b09 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -26,6 +26,7 @@
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -47,7 +48,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Icon;
-import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -77,6 +77,7 @@
 import com.android.launcher3.tracing.LauncherTraceProto;
 import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.WindowBounds;
@@ -106,6 +107,7 @@
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
 import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.wm.shell.back.IBackAnimation;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.recents.IRecentTasks;
@@ -166,10 +168,12 @@
                             bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
             IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
                     bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
+            IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
+                    bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
             MAIN_EXECUTOR.execute(() -> {
                 SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
                         splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
-                        launcherUnlockAnimationController);
+                        launcherUnlockAnimationController, backAnimation);
                 TouchInteractionService.this.initInputMonitor();
                 preloadOverview(true /* fromInit */);
             });
@@ -344,8 +348,6 @@
     private InputMonitorCompat mInputMonitorCompat;
     private InputEventReceiver mInputEventReceiver;
 
-    private DisplayManager mDisplayManager;
-
     private TaskbarManager mTaskbarManager;
     private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
 
@@ -357,7 +359,6 @@
         mMainChoreographer = Choreographer.getInstance();
         mAM = ActivityManagerWrapper.getInstance();
         mDeviceState = new RecentsAnimationDeviceState(this, true);
-        mDisplayManager = getSystemService(DisplayManager.class);
         mTaskbarManager = new TaskbarManager(this);
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
 
@@ -399,7 +400,7 @@
     /**
      * Called when the navigation mode changes, guaranteed to be after the device state has updated.
      */
-    private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
+    private void onNavigationModeChanged() {
         initInputMonitor();
         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
     }
@@ -762,7 +763,10 @@
         } else if (gestureState.getRunningTask() == null) {
             return getDefaultInputConsumer();
         } else if (previousGestureState.isRunningAnimationToLauncher()
-                || gestureState.getActivityInterface().isResumed()
+                || (gestureState.getActivityInterface().isResumed()
+                        // with shell-transitions, home is resumed during recents animation, so
+                        // explicitly check against recents animation too.
+                        && !previousGestureState.isRecentsAnimationRunning())
                 || forceOverviewInputConsumer) {
             return createOverviewInputConsumer(
                     previousGestureState, gestureState, event, forceOverviewInputConsumer);
@@ -945,7 +949,7 @@
             pw.println("Input state:");
             pw.println("  mInputMonitorCompat=" + mInputMonitorCompat);
             pw.println("  mInputEventReceiver=" + mInputEventReceiver);
-            SysUINavigationMode.INSTANCE.get(this).dump(pw);
+            DisplayController.INSTANCE.get(this).dump(pw);
             pw.println("TouchState:");
             BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
                     : mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
@@ -959,6 +963,9 @@
             pw.println("ProtoTrace:");
             pw.println("  file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
             mTaskbarManager.dumpLogs("", pw);
+            if (createdOverviewActivity != null) {
+                createdOverviewActivity.getDeviceProfile().dump("", pw);
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index e290be8..ab26d15 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -21,10 +21,8 @@
 import android.view.ViewRootImpl;
 
 import com.android.launcher3.Utilities;
-import com.android.systemui.shared.system.ViewRootImplCompat;
 
 import java.util.function.BooleanSupplier;
-import java.util.function.LongConsumer;
 
 /**
  * Utility class for helpful methods related to {@link View} objects.
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index 9d9ef94..3e01ed0 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -22,9 +22,9 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.RecentsActivity;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
 
@@ -40,8 +40,8 @@
 
     public FallbackNavBarTouchController(RecentsActivity activity) {
         mActivity = activity;
-        SysUINavigationMode.Mode sysUINavigationMode = SysUINavigationMode.getMode(mActivity);
-        if (sysUINavigationMode == SysUINavigationMode.Mode.NO_BUTTON) {
+        NavigationMode sysUINavigationMode = DisplayController.getNavigationMode(mActivity);
+        if (sysUINavigationMode == NavigationMode.NO_BUTTON) {
             NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode,
                     DisplayController.INSTANCE.get(mActivity).getInfo());
             mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index ff175f1..5094d49 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -111,7 +111,8 @@
 
         RecentsState currentState = mActivity.getStateManager().getState();
         if (isSplitSelectionState(state) && !isSplitSelectionState(currentState)) {
-            setter.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
+            setter.add(mRecentsView.createSplitSelectInitAnimation(
+                    state.getTransitionDuration(mActivity)).buildAnim());
         }
 
         Pair<FloatProperty, FloatProperty> taskViewsFloat =
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index c7a8382..e6f73dc 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -27,6 +28,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
@@ -221,6 +223,7 @@
         setOverviewStateEnabled(true);
         setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
         setOverviewFullscreenEnabled(toState.isFullScreen());
+        Log.d(BAD_STATE, "FRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
         setFreezeViewVisibility(true);
     }
 
@@ -232,6 +235,8 @@
         }
         boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
         setOverlayEnabled(isOverlayEnabled);
+        Log.d(BAD_STATE, "FRV onStateTransitionComplete setFreezeVisibility=false, finalState="
+                + finalState);
         setFreezeViewVisibility(false);
 
         if (isOverlayEnabled) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index dbe260a..3785de4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
@@ -22,6 +22,7 @@
 import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.MotionEvent;
 
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.taskbar.TaskbarActivityContext;
 import com.android.quickstep.InputConsumer;
@@ -36,14 +37,20 @@
     private final GestureDetector mLongPressDetector;
     private final float mSquaredTouchSlop;
 
+
     private float mDownX, mDownY;
     private boolean mCanceledUnstashHint;
+    private final float mUnstashArea;
+    private final float mScreenWidth;
 
     public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
             InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
         super(delegate, inputMonitor);
         mTaskbarActivityContext = taskbarActivityContext;
         mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
+        mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx;
+        mUnstashArea = context.getResources()
+                .getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
 
         mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
             @Override
@@ -69,11 +76,13 @@
                 final float y = ev.getRawY();
                 switch (ev.getAction()) {
                     case MotionEvent.ACTION_DOWN:
-                        mDownX = x;
-                        mDownY = y;
-                        mTaskbarActivityContext.startTaskbarUnstashHint(
-                                /* animateForward = */ true);
-                        mCanceledUnstashHint = false;
+                        if (isInArea(x)) {
+                            mDownX = x;
+                            mDownY = y;
+                            mTaskbarActivityContext.startTaskbarUnstashHint(
+                                    /* animateForward = */ true);
+                            mCanceledUnstashHint = false;
+                        }
                         break;
                     case MotionEvent.ACTION_MOVE:
                         if (!mCanceledUnstashHint
@@ -95,10 +104,18 @@
         }
     }
 
+    private boolean isInArea(float x) {
+        float areaFromMiddle = mUnstashArea / 2.0f;
+        float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x);
+        return distFromMiddle < areaFromMiddle;
+    }
+
     private void onLongPressDetected(MotionEvent motionEvent) {
-        if (mTaskbarActivityContext != null
-                && mTaskbarActivityContext.onLongPressToUnstashTaskbar()) {
-            setActive(motionEvent);
+        if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) {
+            boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
+            if (taskBarPressed) {
+                setActive(motionEvent);
+            }
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 1c3e784..5ef9a9b 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -52,6 +52,7 @@
 import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.quickstep.AnimatedFloat;
@@ -110,6 +111,12 @@
         mContentView = findViewById(R.id.content_view);
         mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
 
+        boolean isTablet = InvariantDeviceProfile.INSTANCE.get(getApplicationContext())
+                .getDeviceProfile(this).isTablet;
+        TextView subtitle = findViewById(R.id.subtitle);
+        subtitle.setText(isTablet
+                ? R.string.allset_description_tablet : R.string.allset_description);
+
         TextView tv = findViewById(R.id.navigation_settings);
         tv.setTextColor(accentColor);
         tv.setOnClickListener(v -> {
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
index b797f0c..b3f2354 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
@@ -15,13 +15,21 @@
  */
 package com.android.quickstep.interaction;
 
+import android.content.SharedPreferences;
 import android.view.MotionEvent;
 import android.view.View;
 
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 /** Shows the Home gesture interactive tutorial. */
 public class AssistantGestureTutorialFragment extends TutorialFragment {
+
+    protected AssistantGestureTutorialFragment(
+            SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+        super(sharedPrefs, statsLogManager);
+    }
+
     @Override
     TutorialController createController(TutorialType type) {
         return new AssistantGestureTutorialController(this, type);
@@ -39,4 +47,14 @@
         }
         return super.onTouch(view, motionEvent);
     }
+
+    @Override
+    void logTutorialStepShown() {
+        // No-Op: tutorial step not currently shown to users
+    }
+
+    @Override
+    void logTutorialStepCompleted() {
+        // No-Op: tutorial step not currently shown to users
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 49d8203..b686505 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -57,14 +57,14 @@
     @LayoutRes
     int getMockAppTaskCurrentPageLayoutResId() {
         return mTutorialFragment.isLargeScreen()
-                ? R.layout.gesture_tutorial_foldable_mock_conversation
+                ? R.layout.gesture_tutorial_tablet_mock_conversation
                 : R.layout.gesture_tutorial_mock_conversation;
     }
 
     @LayoutRes
     int getMockAppTaskPreviousPageLayoutResId() {
         return mTutorialFragment.isLargeScreen()
-                ? R.layout.gesture_tutorial_foldable_mock_conversation_list
+                ? R.layout.gesture_tutorial_tablet_mock_conversation_list
                 : R.layout.gesture_tutorial_mock_conversation_list;
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
index f54734d..e7ed0b4 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
@@ -19,12 +19,14 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.content.SharedPreferences;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 import java.util.ArrayList;
@@ -32,6 +34,11 @@
 /** Shows the Back gesture interactive tutorial. */
 public class BackGestureTutorialFragment extends TutorialFragment {
 
+    protected BackGestureTutorialFragment(
+            SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+        super(sharedPrefs, statsLogManager);
+    }
+
     @Nullable
     @Override
     Integer getEdgeAnimationResId() {
@@ -117,4 +124,16 @@
         }
         return super.onTouch(view, motionEvent);
     }
+
+    @Override
+    void logTutorialStepShown() {
+        mStatsLogManager.logger().log(
+                StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_SHOWN);
+    }
+
+    @Override
+    void logTutorialStepCompleted() {
+        mStatsLogManager.logger().log(
+                StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_COMPLETED);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index c2524b1..002e8b7 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep.interaction;
 
+import android.content.SharedPreferences;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -28,6 +29,8 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 import java.util.List;
@@ -46,17 +49,26 @@
     private int mCurrentStep;
     private int mNumSteps;
 
+    private SharedPreferences mSharedPrefs;
+    private StatsLogManager mStatsLogManager;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         requestWindowFeature(Window.FEATURE_NO_TITLE);
         setContentView(R.layout.gesture_tutorial_activity);
 
+        mSharedPrefs = Utilities.getPrefs(this);
+        mStatsLogManager = StatsLogManager.newInstance(getApplicationContext());
+
         Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
         mTutorialSteps = getTutorialSteps(args);
         mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
         mFragment = TutorialFragment.newInstance(
-                mCurrentTutorialStep, args.getBoolean(KEY_GESTURE_COMPLETE, false));
+                mCurrentTutorialStep,
+                args.getBoolean(KEY_GESTURE_COMPLETE, false),
+                mSharedPrefs,
+                mStatsLogManager);
         getSupportFragmentManager().beginTransaction()
                 .add(R.id.gesture_tutorial_fragment_container, mFragment)
                 .commit();
@@ -105,13 +117,6 @@
     }
 
     /**
-     * Closes the tutorial and this activity.
-     */
-    public void closeTutorial() {
-        mFragment.closeTutorial();
-    }
-
-    /**
      * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
      *
      * If there is no following step, the tutorial is closed.
@@ -122,7 +127,8 @@
             return;
         }
         mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
-        mFragment = TutorialFragment.newInstance(mCurrentTutorialStep, false);
+        mFragment = TutorialFragment.newInstance(
+                mCurrentTutorialStep, /* gestureComplete= */ false, mSharedPrefs, mStatsLogManager);
         getSupportFragmentManager().beginTransaction()
                 .replace(R.id.gesture_tutorial_fragment_container, mFragment)
                 .runOnCommit(() -> mFragment.onAttachedToWindow())
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0bc3691..6254313 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -51,7 +51,7 @@
     @Override
     protected int getMockAppTaskLayoutResId() {
         return mTutorialFragment.isLargeScreen()
-                ? R.layout.gesture_tutorial_foldable_mock_webpage
+                ? R.layout.gesture_tutorial_tablet_mock_webpage
                 : R.layout.gesture_tutorial_mock_webpage;
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index 423e66f..e987d5a 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
@@ -18,12 +18,14 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.content.SharedPreferences;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 import java.util.ArrayList;
@@ -31,6 +33,11 @@
 /** Shows the Home gesture interactive tutorial. */
 public class HomeGestureTutorialFragment extends TutorialFragment {
 
+    protected HomeGestureTutorialFragment(
+            SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+        super(sharedPrefs, statsLogManager);
+    }
+
     @Nullable
     @Override
     Integer getEdgeAnimationResId() {
@@ -99,4 +106,16 @@
         releaseFeedbackAnimation();
         return super.onTouch(view, motionEvent);
     }
+
+    @Override
+    void logTutorialStepShown() {
+        mStatsLogManager.logger().log(
+                StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_SHOWN);
+    }
+
+    @Override
+    void logTutorialStepCompleted() {
+        mStatsLogManager.logger().log(
+                StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_COMPLETED);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 851cccf..a8163af 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -47,7 +47,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
-import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
@@ -101,7 +101,7 @@
         }
         mSwipeUpTouchTracker =
                 new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
-                        new NavBarPosition(Mode.NO_BUTTON, displayRotation),
+                        new NavBarPosition(NavigationMode.NO_BUTTON, displayRotation),
                         null /*onInterceptTouch*/, this);
         mMotionPauseDetector = new MotionPauseDetector(context);
 
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index f308f27..09640c6 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -61,7 +61,7 @@
     @Override
     protected int getMockAppTaskLayoutResId() {
         return mTutorialFragment.isLargeScreen()
-                ? R.layout.gesture_tutorial_foldable_mock_conversation_list
+                ? R.layout.gesture_tutorial_tablet_mock_conversation_list
                 : R.layout.gesture_tutorial_mock_conversation_list;
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index f63a945..c7e24db 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -18,12 +18,14 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.content.SharedPreferences;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 import java.util.ArrayList;
@@ -31,6 +33,11 @@
 /** Shows the Overview gesture interactive tutorial. */
 public class OverviewGestureTutorialFragment extends TutorialFragment {
 
+    protected OverviewGestureTutorialFragment(
+            SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+        super(sharedPrefs, statsLogManager);
+    }
+
     @Nullable
     @Override
     Integer getEdgeAnimationResId() {
@@ -111,4 +118,16 @@
         releaseFeedbackAnimation();
         return super.onTouch(view, motionEvent);
     }
+
+    @Override
+    void logTutorialStepShown() {
+        mStatsLogManager.logger().log(
+                StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_SHOWN);
+    }
+
+    @Override
+    void logTutorialStepCompleted() {
+        mStatsLogManager.logger().log(
+                StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_COMPLETED);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
index 955a2f7..92a2731 100644
--- a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
@@ -15,14 +15,21 @@
  */
 package com.android.quickstep.interaction;
 
+import android.content.SharedPreferences;
 import android.view.MotionEvent;
 import android.view.View;
 
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 /** Shows the general navigation gesture sandbox environment. */
 public class SandboxModeTutorialFragment extends TutorialFragment {
 
+    protected SandboxModeTutorialFragment(
+            SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+        super(sharedPrefs, statsLogManager);
+    }
+
     @Override
     TutorialController createController(TutorialType type) {
         return new SandboxModeTutorialController(this, type);
@@ -40,4 +47,14 @@
         }
         return super.onTouch(view, motionEvent);
     }
+
+    @Override
+    void logTutorialStepShown() {
+        // No-Op: tutorial step not currently shown to users
+    }
+
+    @Override
+    void logTutorialStepCompleted() {
+        // No-Op: tutorial step not currently shown to users
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 672687d..b70c411 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -16,7 +16,7 @@
 package com.android.quickstep.interaction;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
@@ -280,6 +280,15 @@
             super(context, deviceState, gestureState);
             mRemoteTargetHandles[0] = new RemoteTargetGluer.RemoteTargetHandle(
                     mRemoteTargetHandles[0].getTaskViewSimulator(), new FakeTransformParams());
+
+            for (RemoteTargetGluer.RemoteTargetHandle handle
+                    : mTargetGluer.getRemoteTargetHandles()) {
+                // Override home screen rotation preference so that home and overview animations
+                // work properly
+                handle.getTaskViewSimulator()
+                        .getOrientationState()
+                        .ignoreAllowHomeRotationPreference();
+            }
         }
 
         void initDp(DeviceProfile dp) {
@@ -336,8 +345,7 @@
                             1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
                             radius, 255,
                             false, /* isOpening */
-                            mFakeIconView, mDp,
-                            false /* isVerticalBarLayout */);
+                            mFakeIconView, mDp);
                     mFakeIconView.setAlpha(1);
                     mFakeTaskView.setAlpha(getWindowAlpha(progress));
                     mFakePreviousTaskView.setAlpha(getWindowAlpha(progress));
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 4145393..6a8894e 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -49,6 +49,7 @@
 import androidx.appcompat.app.AlertDialog;
 import androidx.appcompat.content.res.AppCompatResources;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
@@ -63,7 +64,7 @@
 
     private static final String TAG = "TutorialController";
 
-    private static final float FINGER_DOT_VISIBLE_ALPHA = 0.6f;
+    private static final float FINGER_DOT_VISIBLE_ALPHA = 0.7f;
     private static final float FINGER_DOT_SMALL_SCALE = 0.7f;
     private static final int FINGER_DOT_ANIMATION_DURATION_MILLIS = 500;
 
@@ -73,14 +74,15 @@
     private static final int FEEDBACK_ANIMATION_MS = 133;
     private static final int RIPPLE_VISIBLE_MS = 300;
     private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
-    private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 4000;
+    private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 2000;
     private static final long GESTURE_ANIMATION_PAUSE_DURATION_MILLIS = 1000;
 
     final TutorialFragment mTutorialFragment;
     TutorialType mTutorialType;
     final Context mContext;
 
-    final TextView mCloseButton;
+    final TextView mSkipButton;
+    final Button mDoneButton;
     final ViewGroup mFeedbackView;
     final TextView mFeedbackTitleView;
     final ImageView mEdgeGestureVideoView;
@@ -93,7 +95,6 @@
     final AnimatedTaskView mFakePreviousTaskView;
     final View mRippleView;
     final RippleDrawable mRippleDrawable;
-    final Button mActionButton;
     final TutorialStepIndicator mTutorialStepView;
     final ImageView mFingerDotView;
     private final AlertDialog mSkipTutorialDialog;
@@ -114,8 +115,8 @@
         mContext = mTutorialFragment.getContext();
 
         RootSandboxLayout rootView = tutorialFragment.getRootView();
-        mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
-        mCloseButton.setOnClickListener(button -> showSkipTutorialDialog());
+        mSkipButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
+        mSkipButton.setOnClickListener(button -> showSkipTutorialDialog());
         mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
         mFeedbackTitleView = mFeedbackView.findViewById(
                 R.id.gesture_tutorial_fragment_feedback_title);
@@ -129,7 +130,7 @@
                 rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
         mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
         mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
-        mActionButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
+        mDoneButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
         mTutorialStepView =
                 rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
         mFingerDotView = rootView.findViewById(R.id.gesture_tutorial_finger_dot);
@@ -185,7 +186,9 @@
     @LayoutRes
     protected int getMockHotseatResId() {
         return mTutorialFragment.isLargeScreen()
-                ? R.layout.gesture_tutorial_foldable_mock_hotseat
+                ? (mTutorialFragment.isFoldable()
+                        ? R.layout.gesture_tutorial_foldable_mock_hotseat
+                        : R.layout.gesture_tutorial_tablet_mock_hotseat)
                 : R.layout.gesture_tutorial_mock_hotseat;
     }
 
@@ -319,6 +322,9 @@
     }
 
     void hideFeedback() {
+        if (mFeedbackView.getVisibility() != View.VISIBLE) {
+            return;
+        }
         cancelQueuedGestureAnimation();
         mFeedbackView.clearAnimation();
         mFeedbackView.setVisibility(View.INVISIBLE);
@@ -425,22 +431,22 @@
     }
 
     void updateCloseButton() {
-        mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext)
+        mSkipButton.setTextAppearance(Utilities.isDarkTheme(mContext)
                 ? R.style.TextAppearance_GestureTutorial_Feedback_Subtext
                 : R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
     }
 
     void hideActionButton() {
-        mCloseButton.setVisibility(View.VISIBLE);
+        mSkipButton.setVisibility(View.VISIBLE);
         // Invisible to maintain the layout.
-        mActionButton.setVisibility(View.INVISIBLE);
-        mActionButton.setOnClickListener(null);
+        mDoneButton.setVisibility(View.INVISIBLE);
+        mDoneButton.setOnClickListener(null);
     }
 
     void showActionButton() {
-        mCloseButton.setVisibility(GONE);
-        mActionButton.setVisibility(View.VISIBLE);
-        mActionButton.setOnClickListener(this::onActionButtonClicked);
+        mSkipButton.setVisibility(GONE);
+        mDoneButton.setVisibility(View.VISIBLE);
+        mDoneButton.setOnClickListener(this::onActionButtonClicked);
     }
 
     void hideFakeTaskbar(boolean animateToHotseat) {
@@ -515,20 +521,45 @@
     }
 
     private void updateLayout() {
-        if (mContext != null) {
-            RelativeLayout.LayoutParams feedbackLayoutParams =
-                    (RelativeLayout.LayoutParams) mFeedbackView.getLayoutParams();
-            feedbackLayoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
-                    mTutorialFragment.isLargeScreen()
-                            ? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
-                            : R.dimen.gesture_tutorial_feedback_margin_start_end));
-            feedbackLayoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
-                    mTutorialFragment.isLargeScreen()
-                            ? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
-                            : R.dimen.gesture_tutorial_feedback_margin_start_end));
-
-            mFakeTaskbarView.setVisibility(mTutorialFragment.isLargeScreen() ? View.VISIBLE : GONE);
+        if (mContext == null) {
+            return;
         }
+        RelativeLayout.LayoutParams feedbackLayoutParams =
+                (RelativeLayout.LayoutParams) mFeedbackView.getLayoutParams();
+        feedbackLayoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
+                mTutorialFragment.isLargeScreen()
+                        ? R.dimen.gesture_tutorial_tablet_feedback_margin_start_end
+                        : R.dimen.gesture_tutorial_feedback_margin_start_end));
+        feedbackLayoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
+                mTutorialFragment.isLargeScreen()
+                        ? R.dimen.gesture_tutorial_tablet_feedback_margin_start_end
+                        : R.dimen.gesture_tutorial_feedback_margin_start_end));
+        feedbackLayoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
+                mTutorialFragment.isLargeScreen()
+                        ? R.dimen.gesture_tutorial_tablet_feedback_margin_top
+                        : R.dimen.gesture_tutorial_feedback_margin_top);
+
+        mFakeTaskbarView.setVisibility(mTutorialFragment.isLargeScreen() ? View.VISIBLE : GONE);
+
+        RelativeLayout.LayoutParams hotseatLayoutParams =
+                (RelativeLayout.LayoutParams) mFakeHotseatView.getLayoutParams();
+        if (!mTutorialFragment.isLargeScreen()) {
+            DeviceProfile dp = mTutorialFragment.getDeviceProfile();
+            dp.updateIsSeascape(mContext);
+
+            hotseatLayoutParams.addRule(dp.isLandscape
+                    ? (dp.isSeascape()
+                            ? RelativeLayout.ALIGN_PARENT_START
+                            : RelativeLayout.ALIGN_PARENT_END)
+                    : RelativeLayout.ALIGN_PARENT_BOTTOM);
+        } else {
+            hotseatLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT;
+            hotseatLayoutParams.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
+            hotseatLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+            hotseatLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
+            hotseatLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
+        }
+        mFakeHotseatView.setLayoutParams(hotseatLayoutParams);
     }
 
     private AlertDialog createSkipTutorialDialog() {
@@ -578,7 +609,7 @@
                     R.id.gesture_tutorial_dialog_confirm_button);
             if (confirmButton != null) {
                 confirmButton.setOnClickListener(v -> {
-                    sandboxActivity.closeTutorial();
+                    mTutorialFragment.closeTutorial(true);
                     tutorialDialog.dismiss();
                 });
             } else {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 2fd7cde..d79b946 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -20,12 +20,13 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
+import android.content.SharedPreferences;
 import android.graphics.Insets;
 import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -41,16 +42,27 @@
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
+import java.util.Set;
+
 abstract class TutorialFragment extends Fragment implements OnTouchListener {
 
     private static final String LOG_TAG = "TutorialFragment";
     static final String KEY_TUTORIAL_TYPE = "tutorial_type";
     static final String KEY_GESTURE_COMPLETE = "gesture_complete";
 
+    private static final String TUTORIAL_SKIPPED_PREFERENCE_KEY = "pref_gestureTutorialSkipped";
+    private static final String COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY =
+            "pref_completedTutorialSteps";
+
+    private final SharedPreferences mSharedPrefs;
+    protected final StatsLogManager mStatsLogManager;
+
     TutorialType mTutorialType;
     boolean mGestureComplete = false;
     @Nullable TutorialController mTutorialController = null;
@@ -67,12 +79,19 @@
 
     private boolean mFragmentStopped = false;
 
+    private DeviceProfile mDeviceProfile;
     private boolean mIsLargeScreen;
+    private boolean mIsFoldable;
 
-    public static TutorialFragment newInstance(TutorialType tutorialType, boolean gestureComplete) {
-        TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
+    public static TutorialFragment newInstance(
+            TutorialType tutorialType,
+            boolean gestureComplete,
+            SharedPreferences sharedPrefs,
+            StatsLogManager statsLogManager) {
+        TutorialFragment fragment =
+                getFragmentForTutorialType(tutorialType, sharedPrefs, statsLogManager);
         if (fragment == null) {
-            fragment = new BackGestureTutorialFragment();
+            fragment = new BackGestureTutorialFragment(sharedPrefs, statsLogManager);
             tutorialType = TutorialType.BACK_NAVIGATION;
         }
 
@@ -84,28 +103,36 @@
     }
 
     @Nullable
-    private static TutorialFragment getFragmentForTutorialType(TutorialType tutorialType) {
+    private static TutorialFragment getFragmentForTutorialType(
+            TutorialType tutorialType,
+            SharedPreferences sharedPrefs,
+            StatsLogManager statsLogManager) {
         switch (tutorialType) {
             case BACK_NAVIGATION:
             case BACK_NAVIGATION_COMPLETE:
-                return new BackGestureTutorialFragment();
+                return new BackGestureTutorialFragment(sharedPrefs, statsLogManager);
             case HOME_NAVIGATION:
             case HOME_NAVIGATION_COMPLETE:
-                return new HomeGestureTutorialFragment();
+                return new HomeGestureTutorialFragment(sharedPrefs, statsLogManager);
             case OVERVIEW_NAVIGATION:
             case OVERVIEW_NAVIGATION_COMPLETE:
-                return new OverviewGestureTutorialFragment();
+                return new OverviewGestureTutorialFragment(sharedPrefs, statsLogManager);
             case ASSISTANT:
             case ASSISTANT_COMPLETE:
-                return new AssistantGestureTutorialFragment();
+                return new AssistantGestureTutorialFragment(sharedPrefs, statsLogManager);
             case SANDBOX_MODE:
-                return new SandboxModeTutorialFragment();
+                return new SandboxModeTutorialFragment(sharedPrefs, statsLogManager);
             default:
                 Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
         }
         return null;
     }
 
+    protected TutorialFragment(SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+        mSharedPrefs = sharedPrefs;
+        mStatsLogManager = statsLogManager;
+    }
+
     @Nullable Integer getEdgeAnimationResId() {
         return null;
     }
@@ -139,22 +166,24 @@
         mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
         mNavBarGestureHandler = new NavBarGestureHandler(getContext());
 
-        mIsLargeScreen = InvariantDeviceProfile.INSTANCE.get(getContext())
-                .getDeviceProfile(getContext()).isTablet;
-
-        if (mIsLargeScreen) {
-            ((Activity) getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
-        } else {
-            // Temporary until UI mocks for landscape mode for phones are created.
-            ((Activity) getContext()).setRequestedOrientation(
-                    ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        }
+        mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(getContext())
+                .getDeviceProfile(getContext());
+        mIsLargeScreen = mDeviceProfile.isTablet;
+        mIsFoldable = mDeviceProfile.isTwoPanels;
     }
 
     public boolean isLargeScreen() {
         return mIsLargeScreen;
     }
 
+    public boolean isFoldable() {
+        return mIsFoldable;
+    }
+
+    DeviceProfile getDeviceProfile() {
+        return mDeviceProfile;
+    }
+
     @Override
     public void onDestroy() {
         super.onDestroy();
@@ -296,6 +325,9 @@
 
     @Override
     public boolean onTouch(View view, MotionEvent motionEvent) {
+        if (mTutorialController != null) {
+            mTutorialController.hideFeedback();
+        }
         // Note: Using logical-or to ensure both functions get called.
         return mEdgeBackGestureHandler.onTouch(view, motionEvent)
                 | mNavBarGestureHandler.onTouch(view, motionEvent);
@@ -308,6 +340,7 @@
     }
 
     void onAttachedToWindow() {
+        logTutorialStepShown();
         mEdgeBackGestureHandler.setViewGroupParent(getRootView());
     }
 
@@ -341,8 +374,16 @@
     }
 
     void continueTutorial() {
-        GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
+        Set<String> updatedCompletedSteps = new ArraySet<>(mSharedPrefs.getStringSet(
+                COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY, new ArraySet<>()));
 
+        updatedCompletedSteps.add(mTutorialType.toString());
+
+        mSharedPrefs.edit().putStringSet(
+                COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY, updatedCompletedSteps).apply();
+        logTutorialStepCompleted();
+
+        GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
         if (gestureSandboxActivity == null) {
             closeTutorial();
             return;
@@ -351,6 +392,15 @@
     }
 
     void closeTutorial() {
+        closeTutorial(false);
+    }
+
+    void closeTutorial(boolean tutorialSkipped) {
+        if (tutorialSkipped) {
+            mSharedPrefs.edit().putBoolean(TUTORIAL_SKIPPED_PREFERENCE_KEY, true).apply();
+            mStatsLogManager.logger().log(
+                    StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_SKIPPED);
+        }
         FragmentActivity activity = getActivity();
         if (activity != null) {
             activity.setResult(Activity.RESULT_OK);
@@ -383,6 +433,10 @@
                 || (mTutorialController != null && mTutorialController.isGestureCompleted());
     }
 
+    abstract void logTutorialStepShown();
+
+    abstract void logTutorialStepCompleted();
+
     @Nullable
     private GestureSandboxActivity getGestureSandboxActivity() {
         Context context = getContext();
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
index d880d74..ae0e725 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
@@ -23,7 +23,6 @@
 import android.widget.LinearLayout;
 
 import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -87,8 +86,10 @@
         for (int i = mTotalSteps; i < getChildCount(); i++) {
             removeViewAt(i);
         }
-        int stepIndicatorColor = GraphicsUtils.getAttrColor(
+        int activeStepIndicatorColor = GraphicsUtils.getAttrColor(
                 getContext(), android.R.attr.textColorPrimary);
+        int inactiveStepIndicatorColor = GraphicsUtils.getAttrColor(
+                getContext(), android.R.attr.textColorSecondaryInverse);
         for (int i = 0; i < mTotalSteps; i++) {
             Drawable pageIndicatorPillDrawable = AppCompatResources.getDrawable(
                     getContext(), R.drawable.tutorial_step_indicator_pill);
@@ -107,10 +108,9 @@
             }
             if (pageIndicatorPillDrawable != null) {
                 if (i < mCurrentStep) {
-                    pageIndicatorPillDrawable.setTint(stepIndicatorColor);
+                    pageIndicatorPillDrawable.setTint(activeStepIndicatorColor);
                 } else {
-                    pageIndicatorPillDrawable.setTint(
-                            ColorUtils.setAlphaComponent(stepIndicatorColor, 0x22));
+                    pageIndicatorPillDrawable.setTint(inactiveStepIndicatorColor);
                 }
             }
         }
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index a0cd0d7..bd0250d 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_ENABLED;
 import static com.android.launcher3.model.DeviceGridState.KEY_WORKSPACE_SIZE;
 import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
 
@@ -44,11 +45,11 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SettingsCache;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -60,8 +61,8 @@
  * Utility class to log launcher settings changes
  */
 public class SettingsChangeLogger implements
-        NavigationModeChangeListener, OnSharedPreferenceChangeListener {
-  
+        DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener {
+
     /**
      * Singleton instance
      */
@@ -76,7 +77,7 @@
     private final ArrayMap<String, LoggablePref> mLoggablePrefs;
     private final StatsLogManager mStatsLogManager;
 
-    private Mode mNavMode;
+    private NavigationMode mNavMode;
     private StatsLogManager.LauncherEvent mNotificationDotsEvent;
     private StatsLogManager.LauncherEvent mHomeScreenSuggestionEvent;
 
@@ -84,7 +85,8 @@
         mContext = context;
         mStatsLogManager = StatsLogManager.newInstance(mContext);
         mLoggablePrefs = loadPrefKeys(context);
-        mNavMode = SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+        DisplayController.INSTANCE.get(context).addChangeListener(this);
+        mNavMode = DisplayController.getNavigationMode(context);
 
         getPrefs(context).registerOnSharedPreferenceChangeListener(this);
         getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
@@ -141,9 +143,11 @@
     }
 
     @Override
-    public void onNavigationModeChanged(Mode newMode) {
-        mNavMode = newMode;
-        mStatsLogManager.logger().log(newMode.launcherEvent);
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
+            mNavMode = info.navigationMode;
+            mStatsLogManager.logger().log(mNavMode.launcherEvent);
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 12a638a..f6002ec 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -19,6 +19,7 @@
 import static androidx.core.util.Preconditions.checkNotNull;
 import static androidx.core.util.Preconditions.checkState;
 
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
@@ -42,10 +43,12 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.Attribute;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logger.LauncherAtom.FolderContainer.ParentContainerCase;
 import com.android.launcher3.logger.LauncherAtom.FolderIcon;
 import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.LauncherAttributes;
 import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
 import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer.SearchAttributes;
@@ -80,6 +83,7 @@
 public class StatsLogCompatManager extends StatsLogManager {
 
     private static final String TAG = "StatsLog";
+    private static final String LATENCY_TAG = "StatsLatencyLog";
     private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
     private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
     // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
@@ -88,13 +92,14 @@
     private static final int FOLDER_HIERARCHY_OFFSET = 100;
     private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
     private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
-    private static final int ATTRIBUTE_MULTIPLIER = 100;
 
     /**
      * Flags for converting SearchAttribute to integer value.
      */
-    private static final int SEARCH_ATTRIBUTES_CORRECTED_QUERY = 1;
+    private static final int SEARCH_ATTRIBUTES_CORRECTED_QUERY = 1 << 0;
     private static final int SEARCH_ATTRIBUTES_DIRECT_MATCH = 1 << 1;
+    private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS = 1 << 2;
+    private static final int SEARCH_ATTRIBUTES_ENTRY_STATE_QSB = 1 << 3;
 
     public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
             new CopyOnWriteArrayList<>();
@@ -110,6 +115,11 @@
         return new StatsCompatLogger(mContext, mActivityContext);
     }
 
+    @Override
+    protected StatsLatencyLogger createLatencyLogger() {
+        return new StatsCompatLatencyLogger(mContext, mActivityContext);
+    }
+
     /**
      * Synchronously writes an itemInfo to stats log
      */
@@ -123,8 +133,7 @@
         }
         SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
                 LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
-                info.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
-                        + info.getItemCase().getNumber()  /* target_id */,
+                info.getItemCase().getNumber()  /* target_id */,
                 instanceId.getId() /* instance_id */,
                 0 /* uid */,
                 getPackageName(info) /* package_name */,
@@ -137,11 +146,20 @@
                 getParentPageId(info) /* page_id_parent */,
                 getHierarchy(info) /* hierarchy */,
                 info.getIsWork() /* is_work_profile */,
-                info.getAttribute().getNumber() /* origin */,
+                0 /* origin */,
                 getCardinality(info) /* cardinality */,
                 info.getWidget().getSpanX(),
                 info.getWidget().getSpanY(),
-                getFeatures(info));
+                getFeatures(info),
+                getAttributes(info) /* attributes */
+        );
+    }
+
+    private static byte[] getAttributes(LauncherAtom.ItemInfo itemInfo) {
+        LauncherAttributes.Builder responseBuilder = LauncherAttributes.newBuilder();
+        itemInfo.getItemAttributesList().stream().map(Attribute::getNumber).forEach(
+                responseBuilder::addItemAttributes);
+        return responseBuilder.build().toByteArray();
     }
 
     /**
@@ -153,8 +171,7 @@
         return SysUiStatsLog.buildStatsEvent(
                 SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, // atom ID,
                 LAUNCHER_WORKSPACE_SNAPSHOT.getId(), // event_id = 1;
-                info.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
-                        + info.getItemCase().getNumber(), // item_id = 2;
+                info.getItemCase().getNumber(), // item_id = 2;
                 instanceId == null ? 0 : instanceId.getId(), //instance_id = 3;
                 0, //uid = 4 [(is_uid) = true];
                 getPackageName(info), // package_name = 5;
@@ -167,10 +184,11 @@
                 getParentPageId(info), //page_id_parent = 12 [default = -2];
                 getHierarchy(info), // container_id = 13;
                 info.getIsWork(), // is_work_profile = 14;
-                info.getAttribute().getNumber(), // attribute_id = 15;
+                0, // attribute_id = 15;
                 getCardinality(info), // cardinality = 16;
                 info.getWidget().getSpanX(), // span_x = 17 [default = 1];
-                info.getWidget().getSpanY() // span_y = 18 [default = 1];
+                info.getWidget().getSpanY(), // span_y = 18 [default = 1];
+                getAttributes(info) /* attributes */
         );
     }
 
@@ -180,7 +198,9 @@
     private static class StatsCompatLogger implements StatsLogger {
 
         private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
-
+        static {
+            DEFAULT_ITEM_INFO.itemType = ITEM_TYPE_NON_ACTIONABLE;
+        }
         private final Context mContext;
         private final Optional<ActivityContext> mActivityContext;
         private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
@@ -372,13 +392,21 @@
             if (IS_VERBOSE) {
                 String name = (event instanceof Enum) ? ((Enum) event).name() :
                         event.getId() + "";
-
-                Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
-                        ? String.format("\n%s (State:%s->%s)\n%s", name, getStateString(srcState),
-                        getStateString(dstState), atomInfo)
-                        : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
-                                getStateString(srcState), getStateString(dstState), instanceId,
-                                atomInfo));
+                StringBuilder logStringBuilder = new StringBuilder("\n");
+                if (instanceId != DEFAULT_INSTANCE_ID) {
+                    logStringBuilder.append(String.format("InstanceId:%s ", instanceId));
+                }
+                logStringBuilder.append(name);
+                if (srcState != LAUNCHER_STATE_UNSPECIFIED
+                        || dstState != LAUNCHER_STATE_UNSPECIFIED) {
+                    logStringBuilder.append(
+                            String.format("(State:%s->%s)", getStateString(srcState),
+                                    getStateString(dstState)));
+                }
+                if (mItemInfo != DEFAULT_ITEM_INFO) {
+                    logStringBuilder.append("\n").append(atomInfo);
+                }
+                Log.d(TAG, logStringBuilder.toString());
             }
 
             for (StatsLogConsumer consumer : LOGS_CONSUMER) {
@@ -393,8 +421,7 @@
                     null /* launcher extensions, deprecated */,
                     false /* quickstep_enabled, deprecated */,
                     event.getId() /* event_id */,
-                    atomInfo.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
-                            + atomInfo.getItemCase().getNumber() /* target_id */,
+                    atomInfo.getItemCase().getNumber() /* target_id */,
                     instanceId.getId() /* instance_id TODO */,
                     0 /* uid TODO */,
                     getPackageName(atomInfo) /* package_name */,
@@ -413,7 +440,71 @@
                     atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
                     getCardinality(atomInfo) /* cardinality */,
                     getFeatures(atomInfo) /* features */,
-                    getSearchAttributes(atomInfo) /* searchAttributes */
+                    getSearchAttributes(atomInfo) /* searchAttributes */,
+                    getAttributes(atomInfo) /* attributes */
+            );
+        }
+    }
+
+    /**
+     * Helps to construct and log statsd compatible latency events.
+     */
+    private static class StatsCompatLatencyLogger implements StatsLatencyLogger {
+        private final Context mContext;
+        private final Optional<ActivityContext> mActivityContext;
+        private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
+        private LatencyType mType = LatencyType.UNKNOWN;
+        private int mPackageId = 0;
+        private long mLatencyInMillis;
+
+        StatsCompatLatencyLogger(Context context, ActivityContext activityContext) {
+            mContext = context;
+            mActivityContext = Optional.ofNullable(activityContext);
+        }
+
+        @Override
+        public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
+            this.mInstanceId = instanceId;
+            return this;
+        }
+
+        @Override
+        public StatsLatencyLogger withType(LatencyType type) {
+            this.mType = type;
+            return this;
+        }
+
+        @Override
+        public StatsLatencyLogger withPackageId(int packageId) {
+            this.mPackageId = packageId;
+            return this;
+        }
+
+        @Override
+        public StatsLatencyLogger withLatency(long latencyInMillis) {
+            this.mLatencyInMillis = latencyInMillis;
+            return this;
+        }
+
+        @Override
+        public void log(EventEnum event) {
+            if (IS_VERBOSE) {
+                String name = (event instanceof Enum) ? ((Enum) event).name() :
+                        event.getId() + "";
+                StringBuilder logStringBuilder = new StringBuilder("\n");
+                if (mInstanceId != DEFAULT_INSTANCE_ID) {
+                    logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
+                }
+                logStringBuilder.append(String.format("%s=%sms", name, mLatencyInMillis));
+                Log.d(LATENCY_TAG, logStringBuilder.toString());
+            }
+
+            SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_LATENCY,
+                    event.getId(), // event_id
+                    mInstanceId.getId(), // instance_id
+                    mPackageId, // package_id
+                    mLatencyInMillis, // latency_in_millis
+                    mType.getId() //type
             );
         }
     }
@@ -592,6 +683,12 @@
         if (searchAttributes.getDirectMatch()) {
             response = response | SEARCH_ATTRIBUTES_DIRECT_MATCH;
         }
+        if (searchAttributes.getEntryState() == SearchAttributes.EntryState.ALL_APPS) {
+            response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_ALL_APPS;
+        } else if (searchAttributes.getEntryState() == SearchAttributes.EntryState.QSB) {
+            response = response | SEARCH_ATTRIBUTES_ENTRY_STATE_QSB;
+        }
+
         return response;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 51a9915..63d5b0d 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -48,10 +48,10 @@
 import androidx.core.content.FileProvider;
 
 import com.android.internal.app.ChooserActivity;
+import com.android.internal.util.ScreenshotHelper;
 import com.android.launcher3.BuildConfig;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.utilities.BitmapUtil;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -77,7 +77,8 @@
     public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
             Rect screenshotBounds,
             Insets visibleInsets, Task.TaskKey task) {
-        systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot),
+        systemUiProxy.handleImageBundleAsScreenshot(
+                ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(screenshot),
                 screenshotBounds, visibleInsets, task);
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 91ba909..6f171f9 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -144,6 +144,7 @@
 
         @Override
         public void onTransitionStarted() {
+            mLauncher.getWorkspace().setPivotToScaleWithSelf(mLauncher.getHotseat());
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 302526d..d0856be 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -22,8 +22,9 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
 
 public class LayoutUtils {
 
@@ -32,7 +33,7 @@
      */
     public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
         float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
-        if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) {
+        if (DisplayController.getNavigationMode(context) == NavigationMode.NO_BUTTON) {
             swipeHeight -= dp.getInsets().bottom;
         }
         return swipeHeight;
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
index 449dba8..527a6d2 100644
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -15,27 +15,27 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
 
 import android.view.Surface;
 
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.quickstep.SysUINavigationMode;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 
 /**
  * Utility class to check nav bar position.
  */
 public class NavBarPosition {
 
-    private final SysUINavigationMode.Mode mMode;
+    private final NavigationMode mMode;
     private final int mDisplayRotation;
 
-    public NavBarPosition(SysUINavigationMode.Mode mode, Info info) {
+    public NavBarPosition(NavigationMode mode, Info info) {
         mMode = mode;
         mDisplayRotation = info.rotation;
     }
 
-    public NavBarPosition(SysUINavigationMode.Mode mode, int displayRotation) {
+    public NavBarPosition(NavigationMode mode, int displayRotation) {
         mMode = mode;
         mDisplayRotation = displayRotation;
     }
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 54642a2..fb32581 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
 
 import android.content.SharedPreferences;
 
@@ -34,8 +34,8 @@
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.OnboardingPrefs;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.views.AllAppsEduView;
 
 /**
@@ -51,8 +51,8 @@
             stateManager.addStateListener(new StateListener<LauncherState>() {
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
-                    boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
-                            .get(mLauncher).getMode().hasGestures;
+                    boolean swipeUpEnabled =
+                            DisplayController.getNavigationMode(mLauncher).hasGestures;
                     LauncherState prevState = stateManager.getLastState();
 
                     if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
@@ -88,7 +88,7 @@
             });
         }
 
-        if (SysUINavigationMode.getMode(launcher) == NO_BUTTON
+        if (DisplayController.getNavigationMode(launcher) == NO_BUTTON
                 && FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
             stateManager.addStateListener(new StateListener<LauncherState>() {
                 private static final int MAX_NUM_SWIPES_TO_TRIGGER_EDU = 3;
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index 5c72c8f..edaa326 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -15,10 +15,13 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.util.Log;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 
@@ -27,6 +30,8 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.quickstep.views.RecentsView;
 
+import java.util.Arrays;
+
 public class RecentsAtomicAnimationFactory<ACTIVITY_TYPE extends StatefulActivity, STATE_TYPE>
         extends AtomicAnimationFactory<STATE_TYPE> {
 
@@ -46,8 +51,30 @@
     public Animator createStateElementAnimation(int index, float... values) {
         switch (index) {
             case INDEX_RECENTS_FADE_ANIM:
-                return ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
+                ObjectAnimator alpha = ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
                         RecentsView.CONTENT_ALPHA, values);
+                Log.d(BAD_STATE, "RAAF createStateElementAnimation alpha="
+                        + Arrays.toString(values));
+                alpha.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        Log.d(BAD_STATE, "RAAF createStateElementAnimation onStart");
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        RecentsView recent = mActivity.getOverviewPanel();
+                        float alpha = recent == null ? -1 : RecentsView.CONTENT_ALPHA.get(recent);
+                        Log.d(BAD_STATE, "RAAF createStateElementAnimation onCancel, alpha="
+                                + alpha);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        Log.d(BAD_STATE, "RAAF createStateElementAnimation onEnd");
+                    }
+                });
+                return alpha;
             case INDEX_RECENTS_TRANSLATE_X_ANIM: {
                 RecentsView rv = mActivity.getOverviewPanel();
                 return new SpringAnimationBuilder(mActivity)
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 4f5c368..1631be0 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -102,6 +102,8 @@
     // Whether the swipe gesture is running, so the recents would stay locked in the
     // current orientation
     private static final int FLAG_SWIPE_UP_NOT_RUNNING = 1 << 8;
+    // Ignore shared prefs for home rotation rotation, allowing it in if the activity supports it
+    private static final int FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF = 1 << 9;
 
     private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
             FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
@@ -371,12 +373,17 @@
                 == MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
     }
 
+    public void ignoreAllowHomeRotationPreference() {
+        setFlag(FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF, true);
+    }
+
     public boolean isRecentsActivityRotationAllowed() {
         // Activity rotation is allowed if the multi-simulated-rotation is not supported
         // (fallback recents or tablets) or activity rotation is enabled by various settings.
         return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
                 != MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
-                || (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
+                || (mFlags & (FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF
+                        | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
                         | FLAG_MULTIWINDOW_ROTATION_ALLOWED
                         | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
     }
@@ -599,7 +606,7 @@
             width = Math.min(currentSize.x, currentSize.y);
             height = Math.max(currentSize.x, currentSize.y);
         }
-        return idp.getBestMatch(width, height);
+        return idp.getBestMatch(width, height, mRecentsActivityRotation);
     }
 
     private static String nameAndAddress(Object obj) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index fff55a1..ee69fc3 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -17,6 +17,7 @@
 package com.android.quickstep.util;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.PendingIntent.FLAG_MUTABLE;
 
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -24,12 +25,15 @@
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 
+import android.annotation.NonNull;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
+import android.text.TextUtils;
 import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -45,6 +49,7 @@
 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;
@@ -59,12 +64,13 @@
  */
 public class SplitSelectStateController {
 
+    private final Context mContext;
     private final Handler mHandler;
     private final SystemUiProxy mSystemUiProxy;
     private final StateManager mStateManager;
     private final DepthController mDepthController;
     private @StagePosition int mStagePosition;
-    private PendingIntent mInitialTaskPendingIntent;
+    private Intent mInitialTaskIntent;
     private int mInitialTaskId = INVALID_TASK_ID;
     private int mSecondTaskId = INVALID_TASK_ID;
     private boolean mRecentsAnimationRunning;
@@ -72,10 +78,11 @@
     @Nullable
     private GroupedTaskView mLaunchingTaskView;
 
-    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy,
-            StateManager stateManager, DepthController depthController) {
+    public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
+            DepthController depthController) {
+        mContext = context;
         mHandler = handler;
-        mSystemUiProxy = systemUiProxy;
+        mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
         mStateManager = stateManager;
         mDepthController = depthController;
     }
@@ -86,12 +93,11 @@
     public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) {
         mInitialTaskId = taskId;
         mStagePosition = stagePosition;
-        mInitialTaskPendingIntent = null;
+        mInitialTaskIntent = null;
     }
 
-    public void setInitialTaskSelect(PendingIntent pendingIntent,
-            @StagePosition int stagePosition) {
-        mInitialTaskPendingIntent = pendingIntent;
+    public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition) {
+        mInitialTaskIntent = intent;
         mStagePosition = stagePosition;
         mInitialTaskId = INVALID_TASK_ID;
     }
@@ -99,9 +105,22 @@
     /**
      * To be called after second task selected
      */
-    public void setSecondTaskId(int taskId, Consumer<Boolean> callback) {
-        mSecondTaskId = taskId;
-        launchTasks(mInitialTaskId, mInitialTaskPendingIntent, mSecondTaskId, mStagePosition,
+    public void setSecondTask(Task task, Consumer<Boolean> callback) {
+        mSecondTaskId = task.key.id;
+        final Intent fillInIntent;
+        if (mInitialTaskIntent != null) {
+            fillInIntent = new Intent();
+            if (TextUtils.equals(mInitialTaskIntent.getComponent().getPackageName(),
+                    task.getTopComponent().getPackageName())) {
+                fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            }
+        } else {
+            fillInIntent = null;
+        }
+        final PendingIntent pendingIntent =
+                mInitialTaskIntent == null ? null : PendingIntent.getActivity(mContext, 0,
+                        mInitialTaskIntent, FLAG_MUTABLE);
+        launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
                 callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
     }
 
@@ -113,25 +132,40 @@
         mLaunchingTaskView = groupedTaskView;
         TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
                 groupedTaskView.getTaskIdAttributeContainers();
-        launchTasks(taskIdAttributeContainers[0].getTask().key.id, null,
+        launchTasks(taskIdAttributeContainers[0].getTask().key.id,
                 taskIdAttributeContainers[1].getTask().key.id,
                 taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList,
                 groupedTaskView.getSplitRatio());
     }
 
     /**
+     * To be called when we want to launch split pairs from Overview when split is initiated from
+     * Overview.
+     */
+    public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
+            Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+        launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
+                stagePosition, callback, freezeTaskList, splitRatio);
+    }
+
+    /**
+     * To be called when we want to launch split pairs from Overview. Split can be initiated from
+     * either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
+     * fill in intent with a taskId2 are set.
+     * @param taskPendingIntent is null when split is initiated from Overview
      * @param stagePosition representing location of task1
      */
     public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
-            int taskId2, @StagePosition int stagePosition, Consumer<Boolean> callback,
-            boolean freezeTaskList, float splitRatio) {
+            @Nullable Intent fillInIntent, 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[]{taskId1, taskId2}
                 : new int[]{taskId2, taskId1};
         if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
             RemoteSplitLaunchTransitionRunner animationRunner =
-                    new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2);
+                    new RemoteSplitLaunchTransitionRunner(taskId1, taskPendingIntent, taskId2,
+                            callback);
             mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
                     null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
                     new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
@@ -156,7 +190,7 @@
                         splitRatio, adapter);
             } else {
                 mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
-                        new Intent(), taskId2, mainOpts.toBundle(), null /* sideOptions */,
+                        fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
                         stagePosition, splitRatio, adapter);
             }
         }
@@ -178,19 +212,26 @@
         private final int mInitialTaskId;
         private final PendingIntent mInitialTaskPendingIntent;
         private final int mSecondTaskId;
+        private final Consumer<Boolean> mSuccessCallback;
 
         RemoteSplitLaunchTransitionRunner(int initialTaskId, PendingIntent initialTaskPendingIntent,
-                int secondTaskId) {
+                int secondTaskId, Consumer<Boolean> callback) {
             mInitialTaskId = initialTaskId;
             mInitialTaskPendingIntent = initialTaskPendingIntent;
             mSecondTaskId = secondTaskId;
+            mSuccessCallback = callback;
         }
 
         @Override
-        public void startAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, Runnable finishCallback) {
+        public void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
             TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskId,
-                    mInitialTaskPendingIntent, mSecondTaskId, info, t, finishCallback);
+                    mInitialTaskPendingIntent, mSecondTaskId, info, t, () -> {
+                    finishCallback.run();
+                    if (mSuccessCallback != null) {
+                        mSuccessCallback.accept(true);
+                    }
+                });
             // After successful launch, call resetState
             resetState();
         }
@@ -250,7 +291,7 @@
      */
     public void resetState() {
         mInitialTaskId = INVALID_TASK_ID;
-        mInitialTaskPendingIntent = null;
+        mInitialTaskIntent = null;
         mSecondTaskId = INVALID_TASK_ID;
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mRecentsAnimationRunning = false;
@@ -262,7 +303,7 @@
      *         chosen
      */
     public boolean isSplitSelectActive() {
-        return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskPendingIntent != null)
+        return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null)
                 && mSecondTaskId == INVALID_TASK_ID;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
new file mode 100644
index 0000000..19a48db
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.content.Context;
+import android.view.Display;
+
+import com.android.launcher3.util.window.WindowManagerProxy;
+
+/**
+ * Extension of {@link WindowManagerProxy} with some assumption for the default system Launcher
+ */
+public class SystemWindowManagerProxy extends WindowManagerProxy {
+
+    public SystemWindowManagerProxy(Context context) {
+        super(true);
+    }
+
+    @Override
+    protected String getDisplayId(Display display) {
+        return display.getUniqueId();
+    }
+
+    @Override
+    public boolean isInternalDisplay(Display display) {
+        return display.getType() == Display.TYPE_INTERNAL;
+    }
+
+    @Override
+    public int getRotation(Context context) {
+        return context.getResources().getConfiguration().windowConfiguration.getRotation();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 27a748d..79b15c7 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -306,7 +306,7 @@
 
     private void setBanner(@Nullable View view) {
         mBanner = view;
-        if (view != null) {
+        if (view != null && mTaskView.getRecentsView() != null) {
             setupAndAddBanner();
             setBannerOutline();
         }
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java
new file mode 100644
index 0000000..d869fed
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A child view of {@link com.android.quickstep.views.FloatingTaskView} to draw the thumbnail in a
+ * rounded corner frame. While the purpose of this class sounds similar to
+ * {@link TaskThumbnailView}, it doesn't need a lot of complex logic in {@link TaskThumbnailView}
+ * in relation to moving with {@link RecentsView}.
+ */
+public class FloatingTaskThumbnailView extends View {
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Matrix mMatrix = new Matrix();
+
+    private @Nullable BitmapShader mBitmapShader;
+    private @Nullable Bitmap mBitmap;
+
+    public FloatingTaskThumbnailView(Context context) {
+        this(context, null);
+    }
+
+    public FloatingTaskThumbnailView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingTaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mBitmap == null) {
+            return;
+        }
+
+        // Scale down the bitmap to fix x, and crop in y.
+        float scale = 1.0f * getMeasuredWidth() / mBitmap.getWidth();
+        mMatrix.reset();
+        mMatrix.postScale(scale, scale);
+        mBitmapShader.setLocalMatrix(mMatrix);
+
+        FloatingTaskView parent = (FloatingTaskView) getParent();
+        parent.drawRoundedRect(canvas, mPaint);
+    }
+
+    public void setThumbnail(Bitmap bitmap) {
+        mBitmap = bitmap;
+        if (bitmap != null) {
+            mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mPaint.setShader(mBitmapShader);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index f2f1c3f..fe5e1d0 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -3,11 +3,12 @@
 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;
-import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
@@ -15,7 +16,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
 
@@ -29,16 +29,16 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.util.MultiValueUpdateListener;
-
-import java.util.function.Consumer;
+import com.android.quickstep.util.TaskCornerRadius;
+import com.android.systemui.shared.system.QuickStepContract;
 
 /**
  * Create an instance via
- * {@link #getFloatingTaskView(StatefulActivity, View, Bitmap, Drawable, RectF, Consumer)} to
+ * {@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
- * {@link #addAnimation(PendingAnimation, RectF, Rect, View, boolean)}
+ * {@link #addAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}
  * giving a starting and ending bounds. Currently this is set to use the split placeholder view,
  * but it could be generified.
  *
@@ -46,13 +46,13 @@
  */
 public class FloatingTaskView extends FrameLayout {
 
+    private FloatingTaskThumbnailView mThumbnailView;
     private SplitPlaceholderView mSplitPlaceholderView;
     private RectF mStartingPosition;
     private final StatefulActivity mActivity;
     private final boolean mIsRtl;
-    private final Rect mOutline = new Rect();
+    private final FullscreenDrawParams mFullscreenParams;
     private PagedOrientationHandler mOrientationHandler;
-    private ImageView mImageView;
 
     public FloatingTaskView(Context context) {
         this(context, null);
@@ -66,14 +66,13 @@
         super(context, attrs, defStyleAttr);
         mActivity = BaseActivity.fromContext(context);
         mIsRtl = Utilities.isRtl(getResources());
+        mFullscreenParams = new FullscreenDrawParams(context);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mImageView = findViewById(R.id.thumbnail);
-        mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-        mImageView.setLayerType(LAYER_TYPE_HARDWARE, null);
+        mThumbnailView = findViewById(R.id.thumbnail);
         mSplitPlaceholderView = findViewById(R.id.split_placeholder);
         mSplitPlaceholderView.setAlpha(0);
     }
@@ -86,28 +85,24 @@
                 (InsettableFrameLayout.LayoutParams) getLayoutParams();
 
         mSplitPlaceholderView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
-        positionOut.round(mOutline);
         setPivotX(0);
         setPivotY(0);
 
         // Copy bounds of exiting thumbnail into ImageView
-        mImageView.setImageBitmap(thumbnail);
-        mImageView.setVisibility(VISIBLE);
+        mThumbnailView.setThumbnail(thumbnail);
+
+        mThumbnailView.setVisibility(VISIBLE);
 
         RecentsView recentsView = launcher.getOverviewPanel();
         mOrientationHandler = recentsView.getPagedOrientationHandler();
         mSplitPlaceholderView.setIcon(icon,
-                launcher.getDeviceProfile().overviewTaskIconDrawableSizePx);
+                mContext.getResources().getDimensionPixelSize(R.dimen.split_placeholder_icon_size));
         mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
     }
 
     /**
      * Configures and returns a an instance of {@link FloatingTaskView} initially matching the
      * appearance of {@code originalView}.
-     *
-     * @param additionalOffsetter optional, to set additional offsets to the FloatingTaskView
-     *                               to account for translations. If {@code null} then the
-     *                               translation values from originalView will be used
      */
     public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
             View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) {
@@ -133,27 +128,25 @@
         setLayoutParams(lp);
     }
 
-    // TODO(194414938) set correct corner radii
-    public void update(RectF position, float progress, float windowRadius) {
+    public void update(RectF bounds, float progress) {
         MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
 
-        float dX = position.left - mStartingPosition.left;
-        float dY = position.top - lp.topMargin;
+        float dX = bounds.left - mStartingPosition.left;
+        float dY = bounds.top - lp.topMargin;
+        float scaleX = bounds.width() / lp.width;
+        float scaleY = bounds.height() / lp.height;
+
+        mFullscreenParams.updateParams(bounds, progress, scaleX, scaleY);
 
         setTranslationX(dX);
         setTranslationY(dY);
-
-        float scaleX = position.width() / lp.width;
-        float scaleY = position.height() / lp.height;
         setScaleX(scaleX);
         setScaleY(scaleY);
+        mSplitPlaceholderView.invalidate();
+        mThumbnailView.invalidate();
+
         float childScaleX = 1f / scaleX;
         float childScaleY = 1f / scaleY;
-
-        invalidate();
-        // TODO(194414938) seems like this scale value could be fine tuned, some stretchiness
-        mImageView.setScaleX(1f / scaleX + scaleX * progress);
-        mImageView.setScaleY(1f / scaleY + scaleY * progress);
         mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIconView(), childScaleX);
         mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY);
     }
@@ -181,7 +174,8 @@
     }
 
     public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
-            boolean fadeWithThumbnail) {
+            boolean fadeWithThumbnail, boolean isStagedTask) {
+        mFullscreenParams.setIsStagedTask(isStagedTask);
         final BaseDragLayer dragLayer = mActivity.getDragLayer();
         int[] dragLayerBounds = new int[2];
         dragLayer.getLocationOnScreen(dragLayerBounds);
@@ -191,22 +185,16 @@
         ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1);
         animation.add(transitionAnimator);
         long animDuration = animation.getDuration();
-        Rect crop = new Rect();
         RectF floatingTaskViewBounds = new RectF();
-        final float initialWindowRadius = supportsRoundedCornersOnWindows(getResources())
-                ? Math.max(crop.width(), crop.height()) / 2f
-                : 0f;
 
         if (fadeWithThumbnail) {
             animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
                     0, 1, ACCEL);
-            animation.addFloat(mImageView, LauncherAnimUtils.VIEW_ALPHA,
+            animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
                     1, 0, DEACCEL_3);
         }
 
         MultiValueUpdateListener listener = new MultiValueUpdateListener() {
-            final FloatProp mWindowRadius = new FloatProp(initialWindowRadius,
-                    initialWindowRadius, 0, animDuration, LINEAR);
             final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
             final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
             final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
@@ -221,12 +209,31 @@
                 Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value,
                         mTaskViewScaleY.value);
 
-                update(floatingTaskViewBounds, percent, mWindowRadius.value * 1);
+                update(floatingTaskViewBounds, percent);
             }
         };
         transitionAnimator.addUpdateListener(listener);
     }
 
+    public void drawRoundedRect(Canvas canvas, Paint paint) {
+        if (mFullscreenParams == null) {
+            return;
+        }
+
+        canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+                mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX,
+                mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY,
+                paint);
+    }
+
+    public float getFullscreenScaleX() {
+        return mFullscreenParams.mScaleX;
+    }
+
+    public float getFullscreenScaleY() {
+        return mFullscreenParams.mScaleY;
+    }
+
     private static class SplitOverlayProperties {
 
         private final float finalTaskViewScaleX;
@@ -250,4 +257,34 @@
             dY = centerY - startTaskViewBounds.centerY();
         }
     }
+
+    public static class FullscreenDrawParams {
+
+        private final float mCornerRadius;
+        private final float mWindowCornerRadius;
+        public boolean mIsStagedTask;
+        public final RectF mBounds = new RectF();
+        public float mCurrentDrawnCornerRadius;
+        public float mScaleX = 1;
+        public float mScaleY = 1;
+
+        public FullscreenDrawParams(Context context) {
+            mCornerRadius = TaskCornerRadius.get(context);
+            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
+
+            mCurrentDrawnCornerRadius = mCornerRadius;
+        }
+
+        public void updateParams(RectF bounds, float progress, float scaleX, float scaleY) {
+            mBounds.set(bounds);
+            mScaleX = scaleX;
+            mScaleY = scaleY;
+            mCurrentDrawnCornerRadius = mIsStagedTask ? mWindowCornerRadius :
+                    Utilities.mapRange(progress, mCornerRadius, mWindowCornerRadius);
+        }
+
+        public void setIsStagedTask(boolean isStagedTask) {
+            mIsStagedTask = isStagedTask;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 04af3c1..b9615ab 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -182,9 +182,8 @@
 
     @Override
     public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
-        getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, null,
-                mSecondaryTask.key.id, STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
-                getSplitRatio());
+        getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, mSecondaryTask.key.id,
+                STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio());
     }
 
     @Override
@@ -272,7 +271,8 @@
 
         getPagedOrientationHandler().setSplitIconParams(mIconView, mIconView2,
                 taskIconHeight, mSnapshotView.getMeasuredWidth(), mSnapshotView.getMeasuredHeight(),
-                isRtl, deviceProfile, mSplitBoundsConfig);
+                getMeasuredHeight(), getMeasuredWidth(), isRtl, deviceProfile,
+                mSplitBoundsConfig);
     }
 
     private void updateSecondaryDwbPlacement() {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index e8f3324..8d717d2 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,11 +21,13 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.Surface;
 
@@ -65,6 +67,7 @@
     public void init(OverviewActionsView actionsView,
             SplitSelectStateController splitPlaceholderView) {
         super.init(actionsView, splitPlaceholderView);
+        Log.d(BAD_STATE, "LauncherRecentsView init setContentAlpha=0");
         setContentAlpha(0);
     }
 
@@ -97,6 +100,7 @@
         setOverviewStateEnabled(toState.overviewUi);
         setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
         setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
+        Log.d(BAD_STATE, "LRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
         setFreezeViewVisibility(true);
     }
 
@@ -108,6 +112,8 @@
         }
         boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
         setOverlayEnabled(isOverlayEnabled);
+        Log.d(BAD_STATE, "LRV onStateTransitionComplete setFreezeVisibility=false, finalState="
+                + finalState);
         setFreezeViewVisibility(false);
 
         if (isOverlayEnabled) {
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 6a3286b..6b15807 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,7 +16,7 @@
 
 package com.android.quickstep.views;
 
-import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -36,10 +36,10 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
 import com.android.quickstep.util.LayoutUtils;
 
@@ -148,13 +148,13 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+        updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
     }
 
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
-        updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+        updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
         updatePaddingAndTranslations();
     }
 
@@ -204,7 +204,7 @@
      */
     private void updatePaddingAndTranslations() {
         boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent &&
-                SysUINavigationMode.getMode(getContext()) == THREE_BUTTONS;
+                DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS;
         if (alignFor3ButtonTaskbar) {
             // Add extra horizontal spacing
             int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext());
@@ -216,14 +216,12 @@
 
             // 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);
+                    DisplayController.getNavigationMode(getContext()), mDp);
             int actionsTop =
-                    (mDp.heightPx - marginBottom - mInsets.bottom) - startActionView.getHeight();
+                    (mDp.heightPx - marginBottom - mInsets.bottom) - mDp.overviewActionsHeight;
             int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY());
-            int transY = navTop - actionsTop
-                    + ((mDp.taskbarSize - startActionView.getHeight()) / 2);
+            int transY = navTop - actionsTop + ((mDp.taskbarSize - mDp.overviewActionsHeight) / 2);
             setTranslationY(transY);
         } else {
             setPadding(mInsets.left, 0, mInsets.right, 0);
@@ -233,7 +231,7 @@
     }
 
     /** Updates vertical margins for different navigation mode or configuration changes. */
-    public void updateVerticalMargin(Mode mode) {
+    public void updateVerticalMargin(NavigationMode mode) {
         if (mDp == null) {
             return;
         }
@@ -249,7 +247,7 @@
      */
     public void setDp(DeviceProfile dp) {
         mDp = dp;
-        updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+        updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
 
         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                 dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing,
@@ -274,14 +272,13 @@
     }
 
     /** Get the top margin associated with the action buttons in Overview. */
-    public static int getOverviewActionsTopMarginPx(
-            SysUINavigationMode.Mode mode, DeviceProfile dp) {
+    public static int getOverviewActionsTopMarginPx(NavigationMode mode, DeviceProfile dp) {
         // In vertical bar, use the smaller task margin for the top regardless of mode
         if (dp.isVerticalBarLayout()) {
             return dp.overviewTaskMarginPx;
         }
 
-        if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) {
+        if (mode == NavigationMode.THREE_BUTTONS) {
             return dp.overviewActionsMarginThreeButtonPx;
         }
 
@@ -289,8 +286,7 @@
     }
 
     /** Get the bottom margin associated with the action buttons in Overview. */
-    public static int getOverviewActionsBottomMarginPx(
-            SysUINavigationMode.Mode mode, DeviceProfile dp) {
+    public static int getOverviewActionsBottomMarginPx(NavigationMode mode, DeviceProfile dp) {
         int inset = dp.getInsets().bottom;
 
         if (dp.isVerticalBarLayout()) {
@@ -298,7 +294,7 @@
         }
 
         // Actions button will be aligned with nav buttons in updatePaddingAndTranslations().
-        if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) {
+        if (mode == NavigationMode.THREE_BUTTONS) {
             return dp.overviewActionsMarginThreeButtonPx + inset;
         }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index cb7e08a..d041589 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -16,7 +16,6 @@
 
 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;
@@ -28,7 +27,6 @@
 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;
@@ -45,7 +43,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.testing.TestProtocol.TASK_VIEW_ID_CRASH;
 import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -69,8 +66,6 @@
 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;
@@ -430,6 +425,7 @@
     private final int mScrollHapticMinGapMillis;
     private final RecentsModel mModel;
     private final int mSplitPlaceholderSize;
+    private final int mSplitPlaceholderInset;
     private final ClearAllButton mClearAllButton;
     private final Rect mClearAllButtonDeadZoneRect = new Rect();
     private final Rect mTaskViewDeadZoneRect = new Rect();
@@ -702,6 +698,8 @@
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         mSplitPlaceholderSize = getResources().getDimensionPixelSize(
                 R.dimen.split_placeholder_size);
+        mSplitPlaceholderInset = getResources().getDimensionPixelSize(
+                R.dimen.split_placeholder_inset);
         mSquaredTouchSlop = squaredTouchSlop(context);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -1083,10 +1081,15 @@
     private int getSnapToLastTaskScrollDiff() {
         // Snap to a position where ClearAll is just invisible.
         int screenStart = mOrientationHandler.getPrimaryScroll(this);
-        int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
         int clearAllScroll = getScrollForPage(indexOfChild(mClearAllButton));
-        int targetScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
-        return screenStart - targetScroll;
+        int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+        int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
+        return screenStart - lastTaskScroll;
+    }
+
+    private int getLastTaskScroll(int clearAllScroll, int clearAllWidth) {
+        int distance = clearAllWidth + getClearAllExtraPageSpacing();
+        return clearAllScroll + (mIsRtl ? distance : -distance);
     }
 
     private int getSnapToFocusedTaskScrollDiff(boolean isClearAllHidden) {
@@ -2013,22 +2016,6 @@
         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);
@@ -2282,8 +2269,6 @@
      * Sets the running task id, cleaning up the old running task if necessary.
      */
     public void setCurrentTask(int runningTaskViewId) {
-        Log.d(TASK_VIEW_ID_CRASH, "currentRunningTaskViewId: " + mRunningTaskViewId
-                + " requestedTaskViewId: " + runningTaskViewId);
         if (mRunningTaskViewId == runningTaskViewId) {
             return;
         }
@@ -2720,7 +2705,7 @@
      */
     private void createInitialSplitSelectAnimation(PendingAnimation anim) {
         mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
-                mActivity.getDeviceProfile(),
+                mSplitPlaceholderInset, mActivity.getDeviceProfile(),
                 mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
 
         RectF startingTaskRect = new RectF();
@@ -2732,7 +2717,7 @@
                     mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
             mFirstFloatingTaskView.setAlpha(1);
             mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
-                    mTempRect, true /*fadeWithThumbnail*/);
+                    mTempRect, true /* fadeWithThumbnail */, true /* isStagedTask */);
         } else {
             mSplitSelectSource.view.setVisibility(INVISIBLE);
             mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
@@ -2740,7 +2725,7 @@
                     mSplitSelectSource.drawable, startingTaskRect);
             mFirstFloatingTaskView.setAlpha(1);
             mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
-                    mTempRect, true /*fadeWithThumbnail*/);
+                    mTempRect, true /* fadeWithThumbnail */, true /* isStagedTask */);
         }
         anim.addEndListener(success -> {
             if (success) {
@@ -3895,9 +3880,10 @@
         float taskSplitScrollOffsetPrimary = 0f;
         float clearAllSplitScrollOffsetPrimar = 0f;
         if (isSplitPlaceholderFirstInGrid()) {
-            taskSplitScrollOffsetPrimary = mSplitPlaceholderSize;
+            taskSplitScrollOffsetPrimary = mIsRtl ? mSplitPlaceholderSize : -mSplitPlaceholderSize;
         } else if (isSplitPlaceholderLastInGrid()) {
-            clearAllSplitScrollOffsetPrimar = -mSplitPlaceholderSize;
+            clearAllSplitScrollOffsetPrimar =
+                    mIsRtl ? -mSplitPlaceholderSize : mSplitPlaceholderSize;
         }
 
         for (int i = 0; i < getTaskViewCount(); i++) {
@@ -3973,28 +3959,17 @@
     }
 
     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),
+        mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
                 splitSelectSource.position.stagePosition);
     }
 
-    public PendingAnimation createSplitSelectInitAnimation() {
+    public PendingAnimation createSplitSelectInitAnimation(int duration) {
         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);
+            PendingAnimation anim = new PendingAnimation(duration);
             createInitialSplitSelectAnimation(anim);
             return anim;
         }
@@ -4030,17 +4005,17 @@
         mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
         mFirstFloatingTaskView.addAnimation(pendingAnimation,
                 new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
-                false /*fadeWithThumbnail*/);
+                false /* fadeWithThumbnail */, true /* isStagedTask */);
 
         mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
                 thumbnailView, thumbnailView.getThumbnail(),
                 iconView.getDrawable(), secondTaskStartingBounds);
         mSecondFloatingTaskView.setAlpha(1);
         mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
-                secondTaskEndingBounds, true /* fadeWithThumbnail */);
+                secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
         pendingAnimation.addEndListener(aBoolean ->
-                mSplitSelectStateController.setSecondTaskId(task.key.id,
-                aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
+                mSplitSelectStateController.setSecondTask(
+                        task, aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
         if (containerTaskView.containsMultipleTasks()) {
             // If we are launching from a child task, then only hide the thumbnail itself
             mSecondSplitHiddenView = thumbnailView;
@@ -4105,12 +4080,11 @@
 
     protected void onRotateInSplitSelectionState() {
         mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
-                mActivity.getDeviceProfile(),
+                mSplitPlaceholderInset, mActivity.getDeviceProfile(),
                 mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
         mTempRectF.set(mTempRect);
-        // TODO(194414938) set correct corner radius
         mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
-        mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f, /*windowRadius=*/0f);
+        mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
 
         PagedOrientationHandler orientationHandler = getPagedOrientationHandler();
         Pair<FloatProperty, FloatProperty> taskViewsFloat =
@@ -4309,7 +4283,7 @@
         }
         mPendingAnimation.addEndListener(isSuccess -> {
             if (isSuccess) {
-                if (tv.getTaskIds()[1] != -1) {
+                if (tv.getTaskIds()[1] != -1 && mRemoteTargetHandles != null) {
                     // TODO(b/194414938): make this part of the animations instead.
                     TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
                             mRemoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
@@ -4548,6 +4522,19 @@
     }
 
     @Override
+    protected int getChildGap(int fromIndex, int toIndex) {
+        int clearAllIndex = indexOfChild(mClearAllButton);
+        return fromIndex == clearAllIndex || toIndex == clearAllIndex
+                ? getClearAllExtraPageSpacing() : 0;
+    }
+
+    private int getClearAllExtraPageSpacing() {
+        return showAsGrid()
+                ? Math.max(mActivity.getDeviceProfile().overviewGridSideMargin - mPageSpacing, 0)
+                : 0;
+    }
+
+    @Override
     protected void updateMinAndMaxScrollX() {
         super.updateMinAndMaxScrollX();
         if (DEBUG) {
@@ -4629,9 +4616,10 @@
             TaskView taskView = requireTaskViewAt(i);
             float scrollDiff = taskView.getScrollAdjustment(showAsFullscreen, showAsGrid);
             int pageScroll = newPageScrolls[i] + (int) scrollDiff;
-            if ((mIsRtl && pageScroll < clearAllScroll + clearAllWidth)
-                    || (!mIsRtl && pageScroll > clearAllScroll - clearAllWidth)) {
-                pageScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
+            int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
+            if ((mIsRtl && pageScroll < lastTaskScroll)
+                    || (!mIsRtl && pageScroll > lastTaskScroll)) {
+                pageScroll = lastTaskScroll;
             }
             if (outPageScrolls[i] != pageScroll) {
                 pageScrollChanged = true;
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index cfa482f..7444217 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -17,16 +17,22 @@
 package com.android.quickstep.views;
 
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
-import android.view.Gravity;
+import android.util.TypedValue;
 import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
 
 public class SplitPlaceholderView extends FrameLayout {
 
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Rect mTempRect = new Rect();
+
     public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
             new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
                 @Override
@@ -46,6 +52,30 @@
 
     public SplitPlaceholderView(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        mPaint.setColor(getThemeBackgroundColor(context));
+        setWillNotDraw(false);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        // Call this before super call to draw below the children.
+        drawBackground(canvas);
+
+        super.dispatchDraw(canvas);
+
+        if (mIconView != null) {
+            // Center the icon view in the visible area.
+            getLocalVisibleRect(mTempRect);
+            FloatingTaskView parent = (FloatingTaskView) getParent();
+            FrameLayout.LayoutParams params =
+                    (FrameLayout.LayoutParams) mIconView.getLayoutParams();
+            params.leftMargin = Math.round(mTempRect.centerX() / parent.getFullscreenScaleX()
+                    - 1.0f * mIconView.getDrawableWidth() / 2);
+            params.topMargin = Math.round(mTempRect.centerY() / parent.getFullscreenScaleY()
+                    - 1.0f * mIconView.getDrawableHeight() / 2);
+            mIconView.setLayoutParams(params);
+        }
     }
 
     @Nullable
@@ -61,7 +91,17 @@
         mIconView.setDrawable(drawable);
         mIconView.setDrawableSize(iconSize, iconSize);
         FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(iconSize, iconSize);
-        params.gravity = Gravity.CENTER;
         mIconView.setLayoutParams(params);
     }
+
+    private void drawBackground(Canvas canvas) {
+        FloatingTaskView parent = (FloatingTaskView) getParent();
+        parent.drawRoundedRect(canvas, mPaint);
+    }
+
+    private static int getThemeBackgroundColor(Context context) {
+        final TypedValue value = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
+        return value.data;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index f716965..ce033e5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -725,7 +725,8 @@
             TestLogging.recordEvent(
                     TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
             ActivityOptionsWrapper opts =  mActivity.getActivityLaunchOptions(this, null);
-            opts.options.setLaunchDisplayId(getRootViewDisplayId());
+            opts.options.setLaunchDisplayId(
+                    getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
             if (ActivityManagerWrapper.getInstance()
                     .startActivityFromRecents(mTask.key, opts.options)) {
                 RecentsView recentsView = getRecentsView();
@@ -766,7 +767,8 @@
             // Indicate success once the system has indicated that the transition has started
             ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
                     getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
-            opts.setLaunchDisplayId(getRootViewDisplayId());
+            opts.setLaunchDisplayId(
+                    getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
             if (freezeTaskList) {
                 ActivityOptionsCompat.setFreezeRecentTasksList(opts);
             }
@@ -888,15 +890,7 @@
                 if (confirmSecondSplitSelectApp()) {
                     return;
                 }
-                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
-                    RecentsView recentsView = getRecentsView();
-                    recentsView.switchToScreenshot(
-                            () -> recentsView.finishRecentsAnimation(true /* toRecents */,
-                                    false /* shouldPip */,
-                                    () -> showTaskMenu(iconView)));
-                } else {
-                    showTaskMenu(iconView);
-                }
+                showTaskMenu(iconView);
             });
             iconView.setOnLongClickListener(v -> {
                 requestDisallowInterceptTouchEvent(true);
@@ -912,31 +906,31 @@
     public void setOrientationState(RecentsOrientedState orientationState) {
         PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
         boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+
         boolean isGridTask = isGridTask();
+        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
+
+        int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
         int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
         int taskMargin = isGridTask ? deviceProfile.overviewTaskMarginGridPx
                 : deviceProfile.overviewTaskMarginPx;
-        int taskIconMargin = snapshotParams.topMargin - taskIconHeight - taskMargin;
-        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        orientationHandler.setIconAndSnapshotParams(mIconView, taskIconMargin, taskIconHeight,
-                snapshotParams, isRtl);
-        updateDwbPlacement();
-        mSnapshotView.setLayoutParams(snapshotParams);
+        int taskIconMargin = thumbnailTopMargin - taskIconHeight - taskMargin;
+        orientationHandler.setTaskIconParams(iconParams, taskIconMargin, taskIconHeight,
+                thumbnailTopMargin, isRtl);
         iconParams.width = iconParams.height = taskIconHeight;
         mIconView.setLayoutParams(iconParams);
+
         mIconView.setRotation(orientationHandler.getDegreesRotated());
         int iconDrawableSize = isGridTask ? deviceProfile.overviewTaskIconDrawableSizeGridPx
                 : deviceProfile.overviewTaskIconDrawableSizePx;
         mIconView.setDrawableSize(iconDrawableSize, iconDrawableSize);
-        snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        mSnapshotView.setLayoutParams(snapshotParams);
-        mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
-    }
 
-    private void updateDwbPlacement() {
+        LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
+        snapshotParams.topMargin = thumbnailTopMargin;
+        mSnapshotView.setLayoutParams(snapshotParams);
+
+        mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
         mDigitalWellBeingToast.initialize(mTask);
     }
 
@@ -1006,8 +1000,11 @@
         // resetViewTransforms is called during Quickswitch scrolling.
         mDismissTranslationX = mTaskOffsetTranslationX =
                 mTaskResistanceTranslationX = mSplitSelectTranslationX = mGridEndTranslationX = 0f;
-        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
-                mSplitSelectTranslationY = 0f;
+        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
+        if (getRecentsView() == null || !getRecentsView().isSplitSelectionActive()) {
+            mSplitSelectTranslationY = 0f;
+        }
+
         setSnapshotScale(1f);
         applyTranslationX();
         applyTranslationY();
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index fb7fda1..09f0858 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -36,7 +36,7 @@
  */
 public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
     static final boolean ENABLE_SHELL_TRANSITIONS =
-            SystemProperties.getBoolean("persist.debug.shell_transit", false);
+            SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
     @Override
     protected TestRule getRulesInsideActivityMonitor() {
         return RuleChain.
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 3e84a76..3bd3722 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -48,7 +48,7 @@
                             Duration.ofSeconds(600), Duration.ofSeconds(300),
                             PendingIntent.getActivity(mTargetContext, -1, new Intent(), 0)));
 
-            mLauncher.pressHome();
+            mLauncher.goHome();
             final DigitalWellBeingToast toast = getToast();
 
             waitForLauncherCondition("Toast is not visible", launcher -> toast.hasLimit());
@@ -58,7 +58,7 @@
             runWithShellPermission(
                     () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
 
-            mLauncher.pressHome();
+            mLauncher.goHome();
             assertFalse("Toast is visible", getToast().hasLimit());
         } finally {
             runWithShellPermission(
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 6be2ce6..4529217 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -61,7 +61,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
@@ -206,7 +205,6 @@
 
     // b/143488140
     //@NavigationModeSwitch
-    @Ignore("b/218403080")
     @Test
     public void testOverview() {
         startAppFast(getAppPackageName());
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 1cb7529..e5e2cf3 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -71,8 +72,8 @@
 
     private final LauncherInstrumentation mLauncher;
 
-    static final SysUINavigationMode SYS_UI_NAVIGATION_MODE =
-            SysUINavigationMode.INSTANCE.get(getInstrumentation().getTargetContext());
+    static final DisplayController DISPLAY_CONTROLLER =
+            DisplayController.INSTANCE.get(getInstrumentation().getTargetContext());
 
     public NavigationModeSwitchRule(LauncherInstrumentation launcher) {
         mLauncher = launcher;
@@ -138,7 +139,7 @@
 
     private static LauncherInstrumentation.NavigationModel currentSysUiNavigationMode() {
         return LauncherInstrumentation.getNavigationModel(
-                SysUINavigationMode.getMode(
+                DisplayController.getNavigationMode(
                         getInstrumentation().
                                 getTargetContext()).
                         resValue);
@@ -159,18 +160,18 @@
         if (currentSysUiNavigationMode() != expectedMode) {
             final CountDownLatch latch = new CountDownLatch(1);
             final Context targetContext = getInstrumentation().getTargetContext();
-            final SysUINavigationMode.NavigationModeChangeListener listener =
-                    newMode -> {
-                        if (LauncherInstrumentation.getNavigationModel(newMode.resValue)
+            final DisplayController.DisplayInfoChangeListener listener =
+                    (context, info, flags) -> {
+                        if (LauncherInstrumentation.getNavigationModel(info.navigationMode.resValue)
                                 == expectedMode) {
                             latch.countDown();
                         }
                     };
             targetContext.getMainExecutor().execute(() ->
-                    SYS_UI_NAVIGATION_MODE.addModeChangeListener(listener));
+                    DISPLAY_CONTROLLER.addChangeListener(listener));
             latch.await(60, TimeUnit.SECONDS);
             targetContext.getMainExecutor().execute(() ->
-                    SYS_UI_NAVIGATION_MODE.removeModeChangeListener(listener));
+                    DISPLAY_CONTROLLER.removeChangeListener(listener));
 
             assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
                     currentSysUiNavigationMode() == expectedMode, description);
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 159a51f..6b3f699 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -21,7 +21,7 @@
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 5351963..6ec6269 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -41,7 +41,7 @@
         super.setUp();
         TaplTestsLauncher3.initialize(this);
         // b/143488140
-        mLauncher.pressHome();
+        mLauncher.goHome();
         // Start an activity where the gestures start.
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
     }
@@ -54,7 +54,7 @@
 
         // The test action.
         eventProcessor.startIteration();
-        mLauncher.pressHome();
+        mLauncher.goHome();
         eventProcessor.finishIteration();
     }
 
@@ -66,7 +66,7 @@
             closeLauncherActivity();
 
             // The test action.
-            mLauncher.pressHome();
+            mLauncher.goHome();
         }
     }
 
@@ -81,6 +81,6 @@
             mLauncher.getLaunchedAppState().switchToOverview();
         }
         closeLauncherActivity();
-        mLauncher.pressHome();
+        mLauncher.goHome();
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 396cb1b..399cd10 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -102,7 +102,7 @@
     public void testOverview() throws Exception {
         startTestAppsWithCheck();
         // mLauncher.pressHome() also tests an important case of pressing home while in background.
-        Overview overview = mLauncher.pressHome().switchToOverview();
+        Overview overview = mLauncher.goHome().switchToOverview();
         assertTrue("Launcher internal state didn't switch to Overview",
                 isInState(() -> LauncherState.OVERVIEW));
         executeOnLauncher(
@@ -127,7 +127,7 @@
                 getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
 
         // Test opening a task.
-        OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
+        OverviewTask task = mLauncher.goHome().switchToOverview().getCurrentTask();
         assertNotNull("overview.getCurrentTask() returned null (1)", task);
         assertNotNull("OverviewTask.open returned null", task.open());
         assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
@@ -139,7 +139,7 @@
                 isInLaunchedApp(launcher)));
 
         // Test dismissing a task.
-        overview = mLauncher.pressHome().switchToOverview();
+        overview = mLauncher.goHome().switchToOverview();
         assertTrue("Launcher internal state didn't switch to Overview",
                 isInState(() -> LauncherState.OVERVIEW));
         final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
@@ -151,7 +151,7 @@
                         numTasks - 1, getTaskCount(launcher)));
 
         // Test dismissing all tasks.
-        mLauncher.pressHome().switchToOverview().dismissAllTasks();
+        mLauncher.goHome().switchToOverview().dismissAllTasks();
         assertTrue("Launcher internal state is not Home",
                 isInState(() -> LauncherState.NORMAL));
         executeOnLauncher(
@@ -168,14 +168,14 @@
     @ScreenRecord // b/195673272
     public void testOverviewActions() throws Exception {
         // Experimenting for b/165029151:
-        final Overview overview = mLauncher.pressHome().switchToOverview();
+        final Overview overview = mLauncher.goHome().switchToOverview();
         if (overview.hasTasks()) overview.dismissAllTasks();
-        mLauncher.pressHome();
+        mLauncher.goHome();
         //
 
         startTestAppsWithCheck();
         OverviewActions actionsView =
-                mLauncher.pressHome().switchToOverview().getOverviewActions();
+                mLauncher.goHome().switchToOverview().getOverviewActions();
         actionsView.clickAndDismissScreenshot();
     }
 
@@ -200,7 +200,7 @@
     @PortraitLandscape
     public void testSwitchToOverview() throws Exception {
         assertNotNull("Workspace.switchToOverview() returned null",
-                mLauncher.pressHome().switchToOverview());
+                mLauncher.goHome().switchToOverview());
         assertTrue("Launcher internal state didn't switch to Overview",
                 isInState(() -> LauncherState.OVERVIEW));
     }
@@ -240,7 +240,7 @@
         // Testing pressHome.
         assertTrue("Launcher internal state is not All Apps",
                 isInState(() -> LauncherState.ALL_APPS));
-        assertNotNull("pressHome returned null", mLauncher.pressHome());
+        assertNotNull("pressHome returned null", mLauncher.goHome());
         assertTrue("Launcher internal state is not Home",
                 isInState(() -> LauncherState.NORMAL));
         assertNotNull("getHome returned null", mLauncher.getWorkspace());
@@ -289,7 +289,7 @@
     @PortraitLandscape
     public void testQuickSwitchFromHome() throws Exception {
         startTestActivity(2);
-        mLauncher.pressHome().quickSwitchToPreviousApp();
+        mLauncher.goHome().quickSwitchToPreviousApp();
         assertTrue("The most recent task is not running after quick switching from home",
                 isTestActivityRunning(2));
         getAndAssertLaunchedApp();
@@ -329,7 +329,7 @@
             startTestActivity(i);
         }
 
-        Overview overview = mLauncher.pressHome().switchToOverview();
+        Overview overview = mLauncher.goHome().switchToOverview();
         executeOnLauncher(
                 launcher -> assertTrue("Don't have at least 13 tasks",
                         getTaskCount(launcher) >= 13));
@@ -348,7 +348,7 @@
                         DEFAULT_UI_TIMEOUT));
 
         // Scroll the task offscreen as it is now first
-        overview = mLauncher.pressHome().switchToOverview();
+        overview = mLauncher.goHome().switchToOverview();
         overview.scrollCurrentTaskOffScreen();
         assertTrue("Launcher internal state is not Overview",
                 isInState(() -> LauncherState.OVERVIEW));
@@ -377,7 +377,7 @@
                         launcher)) <= 1)));
 
         // Test dismissing all tasks.
-        mLauncher.pressHome().switchToOverview().dismissAllTasks();
+        mLauncher.goHome().switchToOverview().dismissAllTasks();
         assertTrue("Launcher internal state is not Home",
                 isInState(() -> LauncherState.NORMAL));
         executeOnLauncher(
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
new file mode 100644
index 0000000..ba93975
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.content.Intent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.tapl.Taskbar;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsTaskbar extends AbstractQuickStepTest {
+
+    private static final String TEST_APP_NAME = "LauncherTestApp";
+    private static final String TEST_APP_PACKAGE =
+            getInstrumentation().getContext().getPackageName();
+    private static final String CALCULATOR_APP_PACKAGE =
+            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+    @Override
+    public void setUp() throws Exception {
+        Assume.assumeTrue(mLauncher.isTablet());
+        super.setUp();
+        mLauncher.useTestWorkspaceLayoutOnReload();
+        TaplTestsLauncher3.initialize(this);
+
+        startAppFast(CALCULATOR_APP_PACKAGE);
+        mLauncher.showTaskbarIfHidden();
+    }
+
+    @After
+    public void tearDown() {
+        mLauncher.useDefaultWorkspaceLayoutOnReload();
+    }
+
+    @Test
+    public void testHideShowTaskbar() {
+        getTaskbar().hide();
+        mLauncher.getLaunchedAppState().showTaskbar();
+    }
+
+    @Test
+    public void testLaunchApp() throws Exception {
+        getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+    }
+
+    @Test
+    public void testOpenMenu() throws Exception {
+        getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
+    }
+
+    @Test
+    public void testLaunchShortcut() throws Exception {
+        getTaskbar().getAppIcon(TEST_APP_NAME)
+                .openDeepShortcutMenu()
+                .getMenuItem("Shortcut 1")
+                .launch(TEST_APP_PACKAGE);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testLaunchAppInSplitscreen() throws Exception {
+        getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
+                TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testLaunchShortcutInSplitscreen() throws Exception {
+        getTaskbar().getAppIcon(TEST_APP_NAME)
+                .openDeepShortcutMenu()
+                .getMenuItem("Shortcut 1")
+                .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+    }
+
+    @Test
+    public void testLaunchApp_FromTaskbarAllApps() throws Exception {
+        getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+    }
+
+    @Test
+    public void testOpenMenu_FromTaskbarAllApps() throws Exception {
+        getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
+    }
+
+    @Test
+    public void testLaunchShortcut_FromTaskbarAllApps() throws Exception {
+        getTaskbar().openAllApps()
+                .getAppIcon(TEST_APP_NAME)
+                .openDeepShortcutMenu()
+                .getMenuItem("Shortcut 1")
+                .launch(TEST_APP_PACKAGE);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
+        getTaskbar().openAllApps()
+                .getAppIcon(TEST_APP_NAME)
+                .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
+        getTaskbar().openAllApps()
+                .getAppIcon(TEST_APP_NAME)
+                .openDeepShortcutMenu()
+                .getMenuItem("Shortcut 1")
+                .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+    }
+
+    private Taskbar getTaskbar() {
+        Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
+        List<String> taskbarIconNames = taskbar.getIconNames();
+        List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
+
+        assertEquals("Taskbar and hotseat icon counts do not match",
+                taskbarIconNames.size(), hotseatIconNames.size());
+
+        for (int i = 0; i < taskbarIconNames.size(); i++) {
+            assertEquals("Taskbar and Hotseat icons do not match",
+                    taskbarIconNames, hotseatIconNames);
+        }
+
+        return taskbar;
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 661beda..7e408a8 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -197,7 +197,7 @@
 
             addItemToScreen(item);
             assertTrue("Widget is not present",
-                    mLauncher.pressHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
+                    mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
             int widgetId = item.appWidgetId;
 
             // Verify widget id
@@ -221,7 +221,7 @@
 
             // Widget is updated when going home
             mInitTracker.disableLog();
-            mLauncher.pressHome();
+            mLauncher.goHome();
             verifyWidget(finalWidgetText);
             assertNotEquals(1, mInitTracker.viewInitCount);
         } finally {
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 9c5cfcd..c6cdafc 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -15,16 +15,15 @@
  */
 package com.android.quickstep.util;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.util.ArrayMap;
+import android.util.Pair;
 import android.view.Display;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -38,8 +37,11 @@
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.LauncherModelHelper;
 import com.android.launcher3.util.ReflectionHelpers;
+import com.android.launcher3.util.RotationUtils;
+import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.util.window.CachedDisplayInfo;
+import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.quickstep.FallbackActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -143,30 +145,34 @@
             LauncherModelHelper helper = new LauncherModelHelper();
             try {
                 helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
-                helper.sandboxContext.allow(SysUINavigationMode.INSTANCE);
+                int rotation = mDisplaySize.x > mDisplaySize.y
+                        ? Surface.ROTATION_90 : Surface.ROTATION_0;
+                CachedDisplayInfo cdi =
+                        new CachedDisplayInfo("test-display", mDisplaySize, rotation , new Rect());
+                WindowBounds wm = new WindowBounds(
+                        new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
+                        mDisplayInsets);
+                WindowBounds[] allBounds = new WindowBounds[4];
+                for (int i = 0; i < 4; i++) {
+                    Rect boundsR = new Rect(wm.bounds);
+                    Rect insetsR = new Rect(wm.insets);
 
-                Display display = mock(Display.class);
-                doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
-                doReturn(mDisplaySize.x > mDisplaySize.y ? Surface.ROTATION_90 : Surface.ROTATION_0)
-                        .when(display).getRotation();
-                doAnswer(i -> {
-                    ((Point) i.getArgument(0)).set(mDisplaySize.x, mDisplaySize.y);
-                    return null;
-                }).when(display).getRealSize(any());
-                doAnswer(i -> {
-                    Point smallestSize = i.getArgument(0);
-                    Point largestSize = i.getArgument(1);
-                    smallestSize.x = smallestSize.y = Math.min(mDisplaySize.x, mDisplaySize.y);
-                    largestSize.x = largestSize.y = Math.max(mDisplaySize.x, mDisplaySize.y);
+                    RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
+                    RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
+                    boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
+                    allBounds[i] = new WindowBounds(boundsR, insetsR);
+                }
 
-                    smallestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
-                    largestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
+                WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
+                doReturn(cdi).when(wmProxy).getDisplayInfo(any());
+                doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any());
 
-                    smallestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
-                    largestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
-                    return null;
-                }).when(display).getCurrentSizeRange(any(), any());
-                DisplayController.Info mockInfo = new Info(helper.sandboxContext, display);
+                ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache =
+                        new ArrayMap<>();
+                perDisplayBoundsCache.put(cdi.id, Pair.create(cdi.normalize(), allBounds));
+
+                DisplayController.Info mockInfo = new Info(
+                        helper.sandboxContext, mock(Display.class), wmProxy, perDisplayBoundsCache);
 
                 DisplayController controller =
                         DisplayController.INSTANCE.get(helper.sandboxContext);
@@ -174,7 +180,7 @@
                 ReflectionHelpers.setField(controller, "mInfo", mockInfo);
 
                 mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
-                        .getBestMatch(mAppBounds.width(), mAppBounds.height());
+                        .getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation);
                 mDeviceProfile.updateInsets(mLauncherInsets);
 
                 TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
diff --git a/res/drawable/drop_target_frame.xml b/res/drawable/drop_target_frame.xml
index 666a96e..9f04103 100644
--- a/res/drawable/drop_target_frame.xml
+++ b/res/drawable/drop_target_frame.xml
@@ -17,6 +17,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
     <solid android:color="@android:color/transparent" />
-    <corners android:radius="28dp" />
+    <corners android:radius="80dp" />
     <stroke android:width="2dp" android:color="?attr/workspaceAccentColor" />
 </shape>
\ No newline at end of file
diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml
index 8d47f4e..e4ca52e 100644
--- a/res/layout/floating_split_select_view.xml
+++ b/res/layout/floating_split_select_view.xml
@@ -4,7 +4,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <ImageView
+    <com.android.quickstep.views.FloatingTaskThumbnailView
         android:id="@+id/thumbnail"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -14,7 +14,6 @@
         android:id="@+id/split_placeholder"
         android:layout_width="match_parent"
         android:layout_height="@dimen/split_placeholder_size"
-        android:background="?android:colorPrimary"
         android:visibility="gone" />
 
 </com.android.quickstep.views.FloatingTaskView>
\ No newline at end of file
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 8571d46..e420d87 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -40,19 +40,19 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"يمكنك النقر على الأداة مع الاستمرار لتحريكها على الشاشة الرئيسية."</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"إضافة إلى الشاشة الرئيسية"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"تمت إضافة الأداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g> إلى الشاشة الرئيسية."</string>
-    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{أداة واحدة}zero{# أداة}two{أداتان}few{# أدوات}many{# أداة}other{# أداة}}"</string>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{تطبيق مصغّر واحد}zero{# تطبيق مصغّر}two{تطبيقان مصغّران}few{# تطبيقات مصغّرة}many{# تطبيقًا مصغّرًا}other{# تطبيق مصغّر}}"</string>
     <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{اختصار واحد}zero{# اختصار}two{اختصاران}few{# اختصارات}many{# اختصارًا}other{# اختصار}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>، <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"بحث"</string>
     <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"محو النص من مربّع البحث"</string>
     <string name="no_widgets_available" msgid="4337693382501046170">"الأدوات والاختصارات غير متاحة."</string>
-    <string name="no_search_results" msgid="3787956167293097509">"لم يتم العثور على أدوات أو اختصارات."</string>
+    <string name="no_search_results" msgid="3787956167293097509">"لم يتم العثور على تطبيقات مصغّرة أو اختصارات."</string>
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"الأدوات الشخصية"</string>
     <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"أدوات العمل"</string>
     <string name="widget_category_conversations" msgid="8894438636213590446">"المحادثات"</string>
     <string name="widget_education_header" msgid="4874760613775913787">"معلومات مفيدة في متناول يديك"</string>
-    <string name="widget_education_content" msgid="745542879510751525">"للحصول على معلومات بدون فتح التطبيقات، يمكنك إضافة الأدوات إلى الشاشة الرئيسية."</string>
+    <string name="widget_education_content" msgid="745542879510751525">"للحصول على معلومات بدون فتح التطبيقات، يمكنك إضافة التطبيقات المصغّرة إلى الشاشة الرئيسية."</string>
     <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"انقر لتغيير إعدادات الأداة"</string>
     <string name="widget_education_close_button" msgid="8676165703104836580">"حسنًا"</string>
     <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغيير إعدادات الأداة"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index a0c784f..1ddd652 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -103,7 +103,7 @@
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
     <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hintergrund &amp; Stil"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Startbildschirm-Einstellungen"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Drehen des Startbildschirms zulassen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Beim Drehen des Smartphones"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index ff0b6c0..2bf2fc7 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -40,8 +40,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tocca e tieni premuto il widget per spostarlo nella schermata Home"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Aggiungi a schermata Home"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> aggiunto alla schermata Home"</string>
-    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widget}}"</string>
-    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}one{# scorciatoia}other{# scorciatoie}}"</string>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}other{# scorciatoie}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Cerca"</string>
@@ -90,7 +90,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Modifica nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"App <xliff:g id="APP_NAME">%1$s</xliff:g> disattivata"</string>
-    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ha # notifica}one{{app_name} ha # notifica}other{{app_name} ha # notifiche}}"</string>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ha # notifica}other{{app_name} ha # notifiche}}"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 662b86e..422240c 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -22,8 +22,22 @@
     <dimen name="fastscroll_popup_text_size">24dp</dimen>
 
     <!-- Dynamic grid -->
+    <dimen name="dynamic_grid_edge_margin">15.28dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
+    <dimen name="dynamic_grid_drop_target_size">36dp</dimen>
+    <dimen name="cell_layout_padding">20dp</dimen>
 
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_side_padding">16dp</dimen>
+    <dimen name="spring_loaded_hotseat_top_margin">45dp</dimen>
+
+    <!-- Dragging -->
+    <dimen name="drop_target_button_gap">28dp</dimen>
+    <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
+    <dimen name="drop_target_button_drawable_vertical_padding">2dp</dimen>
+    <dimen name="drop_target_top_margin">6dp</dimen>
+    <dimen name="drop_target_bottom_margin">6dp</dimen>
+
+    <!-- Workspace grid visualization parameters -->
+    <dimen name="grid_visualization_horizontal_cell_spacing">24dp</dimen>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 1149e02..5612191 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -53,9 +53,9 @@
     <string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
     <string name="widget_education_header" msgid="4874760613775913787">"ଉପଯୋଗୀ ସୂଚନା ଆପଣଙ୍କ ପାଖରେ ସହଜରେ ଉପଲବ୍ଧ"</string>
     <string name="widget_education_content" msgid="745542879510751525">"ଆପଗୁଡ଼ିକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ମୂଳସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string>
-    <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ୍ ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+    <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="widget_education_close_button" msgid="8676165703104836580">"ବୁଝିଗଲି"</string>
-    <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ୍ ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+    <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ୍‌ ଖୋଜନ୍ତୁ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ଆପ୍‌ ଲୋଡ୍‌ ହେଉଛି..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍‌ ମିଳିଲା ନାହିଁ"</string>
@@ -79,10 +79,10 @@
     <string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍‍ ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍‌ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"ହୋମ୍‌ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍‌ ପଢ଼ନ୍ତୁ"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"ହୋମରେ ସେଟିଙ୍ଗ ପଢ଼ିବାକୁ ଆପ ଏବଂ ଶର୍ଟକଟକୁ ଅନୁମତି ଦିଏ।"</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"ହୋମ୍‌ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍‌ ଲେଖନ୍ତୁ"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"ହୋମରେ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍‌ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟ ପଢ଼ନ୍ତୁ"</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"ହୋମରେ ସେଟିଂସ ଏବଂ ସର୍ଟକଟକୁ ପଢ଼ିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟ ଲେଖନ୍ତୁ"</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"ହୋମରେ ସେଟିଂସ ଏବଂ ସର୍ଟକଟକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"ଫୋନ୍‌ କଲ୍‌ କରିବାକୁ <xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
     <string name="gadget_error_text" msgid="740356548025791839">"ୱିଜେଟ୍ ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
     <string name="gadget_setup_text" msgid="8348374825537681407">"ୱିଜେଟ ସେଟିଂସ"</string>
@@ -103,7 +103,7 @@
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍‌"</string>
     <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ୱାଲପେପର୍ ଏବଂ ଷ୍ଟାଇଲ୍"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ୍‌ ସେଟିଂସ୍"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ ସେଟିଂସ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ହୋମ୍‌ ସ୍କ୍ରିନ୍ ବୁଲାଇବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ଯେତେବେଳେ ଫୋନକୁ ବୁଲାଯାଇଥାଏ"</string>
@@ -112,7 +112,7 @@
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"ବିଜ୍ଞପ୍ତି ଆକ୍ସେସ୍‌ ଆବଶ୍ୟକ ଅଟେ"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"ବିଜ୍ଞପ୍ତି ବିନ୍ଦୁ ଦେଖାଇବାକୁ, <xliff:g id="NAME">%1$s</xliff:g> ପାଇଁ ଆପ୍‌ ବିଜ୍ଞପ୍ତି ଅନ୍‌ କରନ୍ତୁ"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+    <string name="title_change_settings" msgid="1376365968844349552">"ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"ବିଜ୍ଞପ୍ତି ଡଟ୍‌ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ହୋମ୍ ସ୍କ୍ରିନରେ ଆପ୍ ଆଇକନଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ନୂଆ ଆପ୍‌ ପାଇଁ"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 0ddef70..85878ad 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -40,8 +40,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Toque sem soltar no widget para o mover à volta do ecrã principal"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Adicionar ao ecrã principal"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado ao ecrã principal"</string>
-    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget(s)}other{# widgets}}"</string>
-    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}one{# atalho(s)}other{# atalhos}}"</string>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}other{# atalhos}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pesquisar"</string>
@@ -90,7 +90,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma app de sistema e não pode ser desinstalada."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edite o nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
-    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A app {app_name} tem # notificação}one{A app {app_name} tem # notificação(ões)}other{A app {app_name} tem # notificações}}"</string>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A app {app_name} tem # notificação}other{A app {app_name} tem # notificações}}"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 4e72d40..07e14f5 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -41,7 +41,7 @@
     <string name="add_to_home_screen" msgid="8631549138215492708">"Добавить на главный экран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" добавлен на главный экран"</string>
     <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}one{# виджет}few{# виджета}many{# виджетов}other{# виджета}}"</string>
-    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# быстрая команда}one{# быстрая команда}few{# быстрые команды}many{# быстрых команд}other{# быстрой команды}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ярлык}one{# ярлык}few{# ярлыка}many{# ярлыков}other{# ярлыка}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Поиск"</string>
@@ -129,7 +129,7 @@
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Добавить на главный экран"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Переместить элемент сюда"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент добавлен на главный экран"</string>
-    <string name="item_removed" msgid="851119963877842327">"Объект удален."</string>
+    <string name="item_removed" msgid="851119963877842327">"Объект убран."</string>
     <string name="undo" msgid="4151576204245173321">"Отменить"</string>
     <string name="action_move" msgid="4339390619886385032">"Переместить элемент"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Переместить в ячейку <xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
new file mode 100644
index 0000000..daca048
--- /dev/null
+++ b/res/values-sw600dp-land/dimens.xml
@@ -0,0 +1,32 @@
+<?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.
+  -->
+
+<resources>
+<!-- Hotseat -->
+    <dimen name="spring_loaded_hotseat_top_margin">44dp</dimen>
+
+<!-- Dynamic grid -->
+    <dimen name="dynamic_grid_edge_margin">11.33dp</dimen>
+    <dimen name="cell_layout_padding">11.33dp</dimen>
+
+<!-- Dragging -->
+    <dimen name="drop_target_top_margin">0dp</dimen>
+    <dimen name="drop_target_bottom_margin">16dp</dimen>
+
+<!-- AllApps -->
+    <dimen name="all_apps_bottom_sheet_horizontal_padding">52dp</dimen>
+</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 92f806e..73edc6f 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -23,7 +23,7 @@
 
 <!-- AllApps -->
     <dimen name="all_apps_search_bar_content_overlap">0dp</dimen>
-    <dimen name="all_apps_bottom_sheet_horizontal_padding">46dp</dimen>
+    <dimen name="all_apps_bottom_sheet_horizontal_padding">48dp</dimen>
 
 <!-- Fast scroll -->
     <dimen name="fastscroll_popup_width">75dp</dimen>
@@ -32,8 +32,22 @@
     <dimen name="fastscroll_popup_text_size">32dp</dimen>
 
 <!-- Dynamic grid -->
+    <dimen name="dynamic_grid_edge_margin">9dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
+    <dimen name="cell_layout_padding">9dp</dimen>
+
 
 <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
+    <dimen name="spring_loaded_hotseat_top_margin">97dp</dimen>
+
+<!-- Dragging -->
+    <dimen name="drop_target_top_margin">34dp</dimen>
+    <dimen name="drop_target_bottom_margin">16dp</dimen>
+    <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
+    <dimen name="drop_target_button_drawable_vertical_padding">16dp</dimen>
+    <dimen name="dynamic_grid_drop_target_size">56dp</dimen>
+
+<!-- Workspace grid visualization parameters -->
+    <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
 </resources>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index a9e0fb8..eb5c751 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -15,6 +15,23 @@
 -->
 
 <resources>
+<!-- Dragging-->
+    <dimen name="drop_target_top_margin">0dp</dimen>
+    <dimen name="drop_target_bottom_margin">32dp</dimen>
+
+<!-- Dynamic grid -->
+    <dimen name="dynamic_grid_edge_margin">21.93dp</dimen>
+    <dimen name="cell_layout_padding">29.33dp</dimen>
+
+<!-- Hotseat -->
+    <dimen name="spring_loaded_hotseat_top_margin">64dp</dimen>
+
 <!-- AllApps -->
-    <dimen name="all_apps_top_padding">0dp</dimen>
+    <dimen name="all_apps_bottom_sheet_horizontal_padding">32dp</dimen>
+
+<!-- Widget picker-->
+    <dimen name="widget_list_horizontal_margin">49dp</dimen>
+
+<!-- Bottom sheet-->
+    <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
 </resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 5c314d5..3ec211a 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,6 +16,27 @@
 
 <resources>
 <!-- AllApps -->
-    <dimen name="all_apps_top_padding">300dp</dimen>
-    <dimen name="all_apps_bottom_sheet_horizontal_padding">65dp</dimen>
+    <dimen name="all_apps_bottom_sheet_horizontal_padding">28dp</dimen>
+
+<!-- Dynamic grid -->
+    <dimen name="dynamic_grid_edge_margin">27.59dp</dimen>
+    <dimen name="cell_layout_padding">36dp</dimen>
+
+<!-- Dragging -->
+    <dimen name="drop_target_text_size">20sp</dimen>
+    <dimen name="dynamic_grid_drop_target_size">72dp</dimen>
+    <dimen name="drop_target_button_drawable_horizontal_padding">24dp</dimen>
+    <dimen name="drop_target_button_drawable_vertical_padding">20dp</dimen>
+    <dimen name="drop_target_button_gap">32dp</dimen>
+    <dimen name="drop_target_top_margin">32dp</dimen>
+    <dimen name="drop_target_bottom_margin">32dp</dimen>
+
+<!-- Hotseat -->
+    <dimen name="spring_loaded_hotseat_top_margin">164dp</dimen>
+
+<!-- Widget picker-->
+    <dimen name="widget_list_horizontal_margin">30dp</dimen>
+
+<!-- Bottom sheet-->
+    <dimen name="bottom_sheet_extra_top_padding">300dp</dimen>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index ce364bd..73c2e1a 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -140,7 +140,7 @@
     <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> గల ఫోల్డర్‌కు జోడించు"</string>
     <string name="added_to_folder" msgid="4793259502305558003">"అంశం ఫోల్డర్‌కు జోడించబడింది"</string>
     <string name="create_folder_with" msgid="4050141361160214248">"ఈ పేరుతో ఫోల్డర్‌ను క్రియేట్ చేయండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"ఫోల్డర్ సృష్టించబడింది"</string>
+    <string name="folder_created" msgid="6409794597405184510">"ఫోల్డర్ క్రియేట్ చేయబడింది"</string>
     <string name="action_move_to_workspace" msgid="1603837886334246317">"హోమ్‌స్క్రీన్‌కు తరలించు"</string>
     <string name="action_resize" msgid="1802976324781771067">"పరిమాణం మార్చు"</string>
     <string name="action_increase_width" msgid="8773715375078513326">"వెడల్పును పెంచు"</string>
diff --git a/res/values-v31/styles.xml b/res/values-v31/styles.xml
index e42d0a3..008a77c 100644
--- a/res/values-v31/styles.xml
+++ b/res/values-v31/styles.xml
@@ -38,6 +38,7 @@
         <item name="preferenceScreenStyle">@style/HomeSettings.PreferenceScreenStyle</item>
         <item name="preferenceStyle">@style/HomeSettings.PreferenceStyle</item>
         <item name="switchPreferenceStyle">@style/HomeSettings.SwitchPreferenceStyle</item>
+        <item name="android:fontFamily">google-sans-text</item>
     </style>
 
     <style name="HomeSettings.CategoryStyle" parent="@style/Preference.Category.Material">
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 99a337e..c96a228 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -152,6 +152,9 @@
 
         <!-- numHotseatIcons defaults to numColumns, if not specified -->
         <attr name="numHotseatIcons" format="integer" />
+        <!-- Number of icons to use when shrinking the hotseat size,
+         defaults to numHotseatIcons / 2 -->
+        <attr name="numShrunkenHotseatIcons" format="integer" />
         <!-- Number of icons to use when extending the hotseat size,
          defaults to 2 * numHotseatIcons -->
         <attr name="numExtendedHotseatIcons" format="integer" />
@@ -212,6 +215,13 @@
         <attr name="borderSpaceHorizontal" format="float" />
         <!-- space below the cell, defaults to borderSpace if not specified -->
         <attr name="borderSpaceVertical" format="float" />
+        <!-- space to be used horizontally and vertically,
+        defaults to borderSpace if not specified -->
+        <attr name="borderSpaceLandscape" format="float" />
+        <!-- space to the right of the cell, defaults to borderSpaceLandscape if not specified -->
+        <attr name="borderSpaceLandscapeHorizontal" format="float" />
+        <!-- space below the cell, defaults to borderSpaceLandscape if not specified -->
+        <attr name="borderSpaceLandscapeVertical" format="float" />
         <!-- space to be used horizontally and vertically in two panels,
         defaults to borderSpace if not specified -->
         <attr name="borderSpaceTwoPanelPortrait" format="float" />
@@ -231,9 +241,28 @@
         if not specified -->
         <attr name="borderSpaceTwoPanelLandscapeVertical" format="float" />
 
+        <!-- These min cell values are only used if GridDisplayOption#isScalable is true -->
+        <!-- defaults to minCellHeight, if not specified -->
+        <attr name="allAppsCellHeight" format="float" />
+        <!-- defaults to minCellWidth, if not specified -->
+        <attr name="allAppsCellWidth" format="float" />
+        <!-- defaults to allAppsCellHeight, if not specified -->
+        <attr name="allAppsCellHeightLandscape" format="float" />
+        <!-- defaults to allAppsCellWidth, if not specified -->
+        <attr name="allAppsCellWidthLandscape" format="float" />
+        <!-- defaults to allAppsCellHeight, if not specified -->
+        <attr name="allAppsCellHeightTwoPanelPortrait" format="float" />
+        <!-- defaults to allAppsCellWidth, if not specified -->
+        <attr name="allAppsCellWidthTwoPanelPortrait" format="float" />
+        <!-- defaults to allAppsCellHeight, if not specified -->
+        <attr name="allAppsCellHeightTwoPanelLandscape" format="float" />
+        <!-- defaults to allAppsCellWidth, if not specified -->
+        <attr name="allAppsCellWidthTwoPanelLandscape" format="float" />
         <!-- defaults to borderSpace, if not specified -->
         <attr name="allAppsBorderSpace" format="float" />
         <!-- defaults to allAppsBorderSpace, if not specified -->
+        <attr name="allAppsBorderSpaceLandscape" format="float" />
+        <!-- defaults to allAppsBorderSpace, if not specified -->
         <attr name="allAppsBorderSpaceTwoPanelPortrait" format="float" />
         <!-- defaults to allAppsBorderSpace, if not specified -->
         <attr name="allAppsBorderSpaceTwoPanelLandscape" format="float" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 1c996eb..5e90bea 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -22,9 +22,6 @@
     <item type="id" name="drag_event_parity" />
 
     <!-- AllApps & Launcher transitions -->
-    <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
-    <integer name="config_workspaceSpringLoadShrinkPercentage">85</integer>
-
     <!-- The duration of the animation from search hint to text entry -->
     <integer name="config_searchHintAnimationDuration">50</integer>
 
@@ -69,6 +66,7 @@
     <string name="test_information_handler_class" translatable="false"></string>
     <string name="launcher_activity_logic_class" translatable="false"></string>
     <string name="model_delegate_class" translatable="false"></string>
+    <string name="window_manager_proxy_class" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
@@ -165,4 +163,8 @@
     <!-- Name of the class used to generate colors from the wallpaper colors. Must be implementing the LauncherAppWidgetHostView.ColorGenerator interface. -->
     <string name="color_generator_class" translatable="false"/>
 
+    <!-- Swipe back to home related -->
+    <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
+    <dimen name="swipe_back_window_scale_y_margin">80dp</dimen>
+    <dimen name="swipe_back_window_corner_radius">40dp</dimen>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3e666fc..0615053 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -19,14 +19,14 @@
     <dimen name="click_shadow_elevation">4dp</dimen>
 
     <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_edge_margin">8dp</dimen>
+    <dimen name="dynamic_grid_edge_margin">10.77dp</dimen>
     <dimen name="dynamic_grid_left_right_margin">8dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
     <!-- Minimum space between workspace and hotseat in spring loaded mode -->
     <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
 
     <dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
-    <dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
+    <dimen name="cell_layout_padding">10.77dp</dimen>
     <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
 
     <!-- Hotseat -->
@@ -34,6 +34,7 @@
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen>
     <dimen name="inline_qsb_bottom_margin">0dp</dimen>
+    <dimen name="spring_loaded_hotseat_top_margin">76dp</dimen>
 
     <!-- Qsb -->
     <!-- Used for adjusting the position of QSB when placed in hotseat. This is a ratio and a higher
@@ -55,8 +56,10 @@
     <dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
 
 <!-- Drop target bar -->
-    <dimen name="dynamic_grid_drop_target_size">52dp</dimen>
+    <dimen name="dynamic_grid_drop_target_size">56dp</dimen>
     <dimen name="drop_target_vertical_gap">20dp</dimen>
+    <dimen name="drop_target_top_margin">36dp</dimen>
+    <dimen name="drop_target_bottom_margin">16dp</dimen>
 
 <!-- App Widget resize frame -->
     <dimen name="widget_handle_margin">13dp</dimen>
@@ -94,7 +97,6 @@
 
     <!-- All Apps -->
     <dimen name="all_apps_starting_vertical_translate">320dp</dimen>
-    <dimen name="all_apps_top_padding">0dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
     <!-- all_apps_search_bar_field_height / 2 -->
     <dimen name="all_apps_search_bar_content_overlap">24dp</dimen>
@@ -207,6 +209,9 @@
     <dimen name="drop_target_shadow_elevation">2dp</dimen>
     <dimen name="drop_target_bar_margin_horizontal">4dp</dimen>
     <dimen name="drop_target_button_drawable_padding">8dp</dimen>
+    <dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
+    <dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
+    <dimen name="drop_target_button_gap">22dp</dimen>
 
     <!-- the distance an icon must be dragged before button drop targets accept it -->
     <dimen name="drag_distanceThreshold">30dp</dimen>
@@ -345,6 +350,7 @@
     <dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
     <dimen name="overview_task_margin">0dp</dimen>
     <dimen name="overview_task_margin_grid">0dp</dimen>
+    <dimen name="overview_actions_height">0dp</dimen>
     <dimen name="overview_actions_button_spacing">0dp</dimen>
     <dimen name="overview_actions_margin_gesture">0dp</dimen>
     <dimen name="overview_actions_top_margin_gesture">0dp</dimen>
@@ -353,13 +359,16 @@
     <dimen name="overview_grid_side_margin">0dp</dimen>
     <dimen name="overview_grid_row_spacing">0dp</dimen>
     <dimen name="overview_page_spacing">0dp</dimen>
-    <dimen name="split_placeholder_size">110dp</dimen>
+    <dimen name="split_placeholder_size">72dp</dimen>
+    <dimen name="split_placeholder_inset">16dp</dimen>
+    <dimen name="split_placeholder_icon_size">44dp</dimen>
     <dimen name="task_menu_width_grid">200dp</dimen>
 
 
 <!-- Workspace grid visualization parameters -->
-    <dimen name="grid_visualization_rounding_radius">22dp</dimen>
-    <dimen name="grid_visualization_cell_spacing">6dp</dimen>
+    <dimen name="grid_visualization_rounding_radius">28dp</dimen>
+    <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
+    <dimen name="grid_visualization_vertical_cell_spacing">6dp</dimen>
 
 <!-- Search results related parameters -->
     <dimen name="search_row_icon_size">48dp</dimen>
@@ -367,6 +376,7 @@
     <dimen name="padded_rounded_button_padding">8dp</dimen>
 
 <!-- Bottom sheet related parameters -->
+    <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
     <dimen name="bottom_sheet_handle_width">32dp</dimen>
     <dimen name="bottom_sheet_handle_height">4dp</dimen>
     <dimen name="bottom_sheet_handle_margin">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 868b5f3..267f9c3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -60,9 +60,9 @@
     <string name="widget_preview_context_description"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget</string>
     <!-- Message to tell the user to press and hold a widget/icon to add it to the home screen.
          [CHAR LIMIT=NONE]  -->
-    <string name="add_item_request_drag_hint">Touch &amp; hold the widget to move it around the Home screen</string>
+    <string name="add_item_request_drag_hint">Touch &amp; hold the widget to move it around the home screen</string>
     <!-- Button label to automatically add a widget to home screen [CHAR_LIMIT=50] -->
-    <string name="add_to_home_screen">Add to Home screen</string>
+    <string name="add_to_home_screen">Add to home screen</string>
     <!-- Accessibility spoken message announced when a widget gets added to the home screen using a
          button in a dialog. [CHAR_LIMIT=none] -->
     <string name="added_to_home_screen_accessibility_text"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget added to home screen</string>
@@ -108,7 +108,7 @@
     <string name="widget_education_header">Useful info at your fingertips</string>
     <!-- Dialog text. This dialog lets a user know how they can use widgets on their phone.
          [CHAR_LIMIT=NONE] -->
-    <string name="widget_education_content">To get info without opening apps, you can add widgets to your Home screen</string>
+    <string name="widget_education_content">To get info without opening apps, you can add widgets to your home screen</string>
 
     <!-- Text on an educational tip on widget informing users that they can change widget settings.
          [CHAR_LIMIT=NONE] -->
@@ -147,7 +147,7 @@
 
     <skip />
     <!-- Error message when a user can't add more apps, widgets, or shortcuts to a Home screen. -->
-    <string name="out_of_space">No room on this Home screen</string>
+    <string name="out_of_space">No room on this home screen</string>
     <!-- Error message when user has filled the hotseat -->
     <string name="hotseat_out_of_space">No more room in the Favorites tray</string>
 
@@ -181,15 +181,15 @@
     <string name="permdesc_install_shortcut">Allows an app to add
         shortcuts without user intervention.</string>
     <!-- Permission short label -->
-    <string name="permlab_read_settings">read Home settings and shortcuts</string>
+    <string name="permlab_read_settings">read home settings and shortcuts</string>
     <!-- Permission description -->
     <string name="permdesc_read_settings">Allows the app to read the settings and
-        shortcuts in Home.</string>
+        shortcuts in home.</string>
     <!-- Permission short label -->
-    <string name="permlab_write_settings">write Home settings and shortcuts</string>
+    <string name="permlab_write_settings">write home settings and shortcuts</string>
     <!-- Permission description -->
     <string name="permdesc_write_settings">Allows the app to change the settings and
-        shortcuts in Home.</string>
+        shortcuts in home.</string>
 
     <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
     <string name="msg_no_phone_permission"><xliff:g id="app_name" example="Launcher3">%1$s</xliff:g> is not allowed to make phone calls</string>
@@ -259,7 +259,7 @@
 
     <!-- Strings for settings -->
     <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
-    <string name="allow_rotation_title">Allow Home screen rotation</string>
+    <string name="allow_rotation_title">Allow home screen rotation</string>
     <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
     <string name="allow_rotation_desc">When phone is rotated</string>
     <!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
@@ -278,7 +278,7 @@
     <string name="notification_dots_service_title">Show notification dots</string>
 
     <!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=60] -->
-    <string name="auto_add_shortcuts_label">Add app icons to Home screen</string>
+    <string name="auto_add_shortcuts_label">Add app icons to home screen</string>
     <!-- Text description of the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=NONE] -->
     <string name="auto_add_shortcuts_description">For new apps</string>
 
@@ -312,7 +312,7 @@
 
     <!-- Strings for accessibility actions -->
     <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
-    <string name="action_add_to_workspace">Add to Home screen</string>
+    <string name="action_add_to_workspace">Add to home screen</string>
 
     <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
     <string name="action_move_here">Move item here</string>
@@ -357,7 +357,7 @@
     <string name="folder_created">Folder created</string>
 
     <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
-    <string name="action_move_to_workspace">Move to Home screen</string>
+    <string name="action_move_to_workspace">Move to home screen</string>
 
     <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
     <string name="action_resize">Resize</string>
diff --git a/res/xml/default_test_workspace.xml b/res/xml/default_test_workspace.xml
new file mode 100644
index 0000000..bd718b3
--- /dev/null
+++ b/res/xml/default_test_workspace.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+    <!-- Launcher Test Activity -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.CATEGORY_TEST;component=com.google.android.apps.nexuslauncher.tests/com.android.launcher3.testcomponent.BaseTestingActivity;end" />
+    </resolve>
+
+</favorites>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index ceb38d0..e75348c 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -103,7 +103,7 @@
     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
             | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG
-            | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS;
+            | TYPE_TASKBAR_EDUCATION_DIALOG;
 
     // Usually we show the back button when a floating view is open. Instead, hide for these types.
     public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 42f7053..73d3e33 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -25,12 +24,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
 
 import androidx.annotation.IntDef;
 
@@ -39,7 +33,7 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
-import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.AppLauncher;
 import com.android.launcher3.views.ScrimView;
 
 import java.io.PrintWriter;
@@ -50,7 +44,7 @@
 /**
  * Launcher BaseActivity
  */
-public abstract class BaseActivity extends Activity implements ActivityContext,
+public abstract class BaseActivity extends Activity implements AppLauncher,
         DeviceProfileListenable {
 
     private static final String TAG = "BaseActivity";
@@ -314,22 +308,6 @@
         writer.println(prefix + "mForceInvisible: " + mForceInvisible);
     }
 
-    /**
-     * A wrapper around the platform method with Launcher specific checks
-     */
-    public void startShortcut(String packageName, String id, Rect sourceBounds,
-            Bundle startActivityOptions, UserHandle user) {
-        if (GO_DISABLE_WIDGETS) {
-            return;
-        }
-        try {
-            getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
-                    startActivityOptions, user);
-        } catch (SecurityException | IllegalStateException e) {
-            Log.e(TAG, "Failed to start shortcut", e);
-        }
-    }
-
     public static <T extends BaseActivity> T fromContext(Context context) {
         if (context instanceof BaseActivity) {
             return (T) context;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index b47554f..6de3884 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,53 +16,33 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.app.ActivityOptions;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.app.WallpaperManager.OnColorsChangedListener;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
-import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Process;
-import android.os.StrictMode;
-import android.os.UserHandle;
-import android.util.Log;
 import android.view.ActionMode;
 import android.view.Display;
 import android.view.View;
-import android.view.WindowInsets.Type;
-import android.view.WindowMetrics;
-import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.InstanceIdSequence;
-import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
@@ -165,112 +145,12 @@
         // no-op
     }
 
+    @Override
     @NonNull
     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
-        int left = 0, top = 0;
-        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-        if (v instanceof BubbleTextView) {
-            // Launch from center of icon, not entire view
-            Drawable icon = ((BubbleTextView) v).getIcon();
-            if (icon != null) {
-                Rect bounds = icon.getBounds();
-                left = (width - bounds.width()) / 2;
-                top = v.getPaddingTop();
-                width = bounds.width();
-                height = bounds.height();
-            }
-        }
-        ActivityOptions options =
-                ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-
-        options.setLaunchDisplayId(
-                (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
-                        : Display.DEFAULT_DISPLAY);
-        RunnableList callback = new RunnableList();
-        addOnResumeCallback(callback::executeAllAndDestroy);
-        return new ActivityOptionsWrapper(options, callback);
-    }
-
-    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
-        if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
-            Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
-            return false;
-        }
-
-        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
-        UserHandle user = item == null ? null : item.user;
-
-        // Prepare intent
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        if (v != null) {
-            intent.setSourceBounds(Utilities.getViewBounds(v));
-        }
-        try {
-            boolean isShortcut = (item instanceof WorkspaceItemInfo)
-                    && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
-                    && !((WorkspaceItemInfo) item).isPromise();
-            if (isShortcut) {
-                // Shortcuts need some special checks due to legacy reasons.
-                startShortcutIntentSafely(intent, optsBundle, item);
-            } else if (user == null || user.equals(Process.myUserHandle())) {
-                // Could be launching some bookkeeping activity
-                startActivity(intent, optsBundle);
-            } else {
-                getSystemService(LauncherApps.class).startMainActivity(
-                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
-            }
-            if (item != null) {
-                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
-                logAppLaunch(getStatsLogManager(), item, instanceId);
-            }
-            return true;
-        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
-        }
-        return false;
-    }
-
-    /**
-     * Creates and logs a new app launch event.
-     */
-    public void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
-            InstanceId instanceId) {
-        statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
-                .log(LAUNCHER_APP_LAUNCH_TAP);
-    }
-
-    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
-        try {
-            StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
-            try {
-                // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
-                // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
-                // is enabled by default on NYC.
-                StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
-                        .penaltyLog().build());
-
-                if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                    String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
-                    String packageName = intent.getPackage();
-                    startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
-                } else {
-                    // Could be launching some bookkeeping activity
-                    startActivity(intent, optsBundle);
-                }
-            } finally {
-                StrictMode.setVmPolicy(oldPolicy);
-            }
-        } catch (SecurityException e) {
-            if (!onErrorStartingShortcut(intent, info)) {
-                throw e;
-            }
-        }
-    }
-
-    protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
-        return false;
+        ActivityOptionsWrapper wrapper = super.getActivityLaunchOptions(v, item);
+        addOnResumeCallback(wrapper.onEndCallback::executeAllAndDestroy);
+        return wrapper;
     }
 
     @Override
@@ -322,11 +202,7 @@
 
     protected WindowBounds getMultiWindowDisplaySize() {
         if (Utilities.ATLEAST_R) {
-            WindowMetrics wm = getWindowManager().getCurrentWindowMetrics();
-
-            Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
-            return new WindowBounds(wm.getBounds(),
-                    new Rect(insets.left, insets.top, insets.right, insets.bottom));
+            return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
         }
         // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
         // the app window size
@@ -340,8 +216,14 @@
      * Creates and returns {@link SearchAdapterProvider} for build variant specific search result
      * views
      */
+    @Override
     public SearchAdapterProvider<?> createSearchAdapterProvider(
             ActivityAllAppsContainerView<?> allApps) {
         return new DefaultSearchAdapterProvider(this);
     }
+
+    @Override
+    public boolean isAppBlockedForSafeMode() {
+        return mIsSafeModeEnabled;
+    }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 041bee9..3bc0f6d 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -867,6 +867,11 @@
     }
 
     protected void applyCompoundDrawables(Drawable icon) {
+        if (icon == null) {
+            // Icon can be null when we use the BubbleTextView for text only.
+            return;
+        }
+
         // If we had already set an icon before, disable relayout as the icon size is the
         // same as before.
         mDisableRelayout = mIcon != null;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ad4cae4..f7133c4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -93,7 +93,7 @@
     private int mFixedCellWidth;
     private int mFixedCellHeight;
     @ViewDebug.ExportedProperty(category = "launcher")
-    private final Point mBorderSpace;
+    private Point mBorderSpace;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mCountX;
@@ -148,7 +148,8 @@
     private boolean mVisualizeDropLocation = true;
     private RectF mVisualizeGridRect = new RectF();
     private Paint mVisualizeGridPaint = new Paint();
-    private int mGridVisualizationPadding;
+    private int mGridVisualizationPaddingX;
+    private int mGridVisualizationPaddingY;
     private int mGridVisualizationRoundingRadius;
     private float mGridAlpha = 0f;
     private int mGridColor = 0;
@@ -238,22 +239,7 @@
         mActivity = ActivityContext.lookupContext(context);
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
 
-        switch (mContainerType) {
-            case FOLDER:
-                mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx);
-                break;
-            case HOTSEAT:
-                mBorderSpace = new Point(deviceProfile.hotseatBorderSpace,
-                        deviceProfile.hotseatBorderSpace);
-                break;
-            case WORKSPACE:
-            default:
-                mBorderSpace = new Point(deviceProfile.cellLayoutBorderSpacePx);
-                break;
-        }
-
-        mCellWidth = mCellHeight = -1;
-        mFixedCellWidth = mFixedCellHeight = -1;
+        resetCellSizeInternal(deviceProfile);
 
         mCountX = deviceProfile.inv.numColumns;
         mCountY = deviceProfile.inv.numRows;
@@ -275,8 +261,10 @@
         mBackground.setAlpha(0);
 
         mGridColor = Themes.getAttrColor(getContext(), R.attr.workspaceAccentColor);
-        mGridVisualizationPadding =
-                res.getDimensionPixelSize(R.dimen.grid_visualization_cell_spacing);
+        mGridVisualizationPaddingX = res.getDimensionPixelSize(
+                R.dimen.grid_visualization_horizontal_cell_spacing);
+        mGridVisualizationPaddingY = res.getDimensionPixelSize(
+                R.dimen.grid_visualization_vertical_cell_spacing);
         mGridVisualizationRoundingRadius =
                 res.getDimensionPixelSize(R.dimen.grid_visualization_rounding_radius);
         mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx);
@@ -376,6 +364,12 @@
         return mShortcutsAndWidgets.getLayerType() == LAYER_TYPE_HARDWARE;
     }
 
+    /**
+     * Change sizes of cells
+     *
+     * @param width  the new width of the cells
+     * @param height the new height of the cells
+     */
     public void setCellDimensions(int width, int height) {
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
@@ -383,6 +377,33 @@
                 mBorderSpace);
     }
 
+    private void resetCellSizeInternal(DeviceProfile deviceProfile) {
+        switch (mContainerType) {
+            case FOLDER:
+                mBorderSpace = new Point(deviceProfile.folderCellLayoutBorderSpacePx);
+                break;
+            case HOTSEAT:
+                mBorderSpace = new Point(deviceProfile.hotseatBorderSpace,
+                        deviceProfile.hotseatBorderSpace);
+                break;
+            case WORKSPACE:
+            default:
+                mBorderSpace = new Point(deviceProfile.cellLayoutBorderSpacePx);
+                break;
+        }
+
+        mCellWidth = mCellHeight = -1;
+        mFixedCellWidth = mFixedCellHeight = -1;
+    }
+
+    /**
+     * Reset the cell sizes and border space
+     */
+    public void resetCellSize(DeviceProfile deviceProfile) {
+        resetCellSizeInternal(deviceProfile);
+        requestLayout();
+    }
+
     public void setGridSize(int x, int y) {
         mCountX = x;
         mCountY = y;
@@ -573,8 +594,8 @@
 
     protected void visualizeGrid(Canvas canvas) {
         DeviceProfile dp = mActivity.getDeviceProfile();
-        int paddingX = (int) Math.min((mCellWidth - dp.iconSizePx) / 2, mGridVisualizationPadding);
-        int paddingY = (int) Math.min((mCellHeight - dp.iconSizePx) / 2, mGridVisualizationPadding);
+        int paddingX = Math.min((mCellWidth - dp.iconSizePx) / 2, mGridVisualizationPaddingX);
+        int paddingY = Math.min((mCellHeight - dp.iconSizePx) / 2, mGridVisualizationPaddingY);
         mVisualizeGridRect.set(paddingX, paddingY,
                 mCellWidth - paddingX,
                 mCellHeight - paddingY);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 97c0f38..009ee27 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -34,7 +34,6 @@
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.DevicePaddings.DevicePadding;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
@@ -59,6 +58,7 @@
 
     // Device properties
     public final boolean isTablet;
+    public final boolean isLargeTablet;
     public final boolean isPhone;
     public final boolean transposeLayoutWithOrientation;
     public final boolean isTwoPanels;
@@ -67,6 +67,7 @@
     // Device properties in current orientation
     public final boolean isLandscape;
     public final boolean isMultiWindowMode;
+    public final boolean isGestureMode;
 
     public final int windowX;
     public final int windowY;
@@ -74,6 +75,7 @@
     public final int heightPx;
     public final int availableWidthPx;
     public final int availableHeightPx;
+    public final int rotationHint;
 
     public final float aspectRatio;
 
@@ -92,18 +94,16 @@
     private static final float TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP = 252;
     private static final float TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP = 268;
 
-    // To evenly space the icons, increase the left/right margins for tablets in portrait mode.
-    private static final int PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER = 4;
-
     // Workspace
     public final int desiredWorkspaceHorizontalMarginOriginalPx;
     public int desiredWorkspaceHorizontalMarginPx;
     public Point cellLayoutBorderSpaceOriginalPx;
     public Point cellLayoutBorderSpacePx;
-    public final int cellLayoutPaddingLeftRightPx;
-    public final int cellLayoutBottomPaddingPx;
+    public Rect cellLayoutPaddingPx = new Rect();
+
     public final int edgeMarginPx;
-    public float workspaceSpringLoadShrinkFactor;
+    public float workspaceSpringLoadShrunkTop;
+    public float workspaceSpringLoadShrunkBottom;
     public final int workspaceSpringLoadedBottomSpace;
 
     private final int extraSpace;
@@ -159,6 +159,7 @@
     public int hotseatBarSizePx;
     public int hotseatBarTopPaddingPx;
     public final int hotseatBarBottomPaddingPx;
+    public int springLoadedHotseatBarTopMarginPx;
     // Start is the side next to the nav bar, end is the side next to the workspace
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
@@ -173,6 +174,7 @@
     public Point allAppsBorderSpacePx;
     public int allAppsShiftRange;
     public int allAppsTopPadding;
+    public int bottomSheetTopPadding;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
     public int allAppsIconSizePx;
@@ -189,6 +191,7 @@
     public int overviewTaskIconDrawableSizePx;
     public int overviewTaskIconDrawableSizeGridPx;
     public int overviewTaskThumbnailTopMarginPx;
+    public final int overviewActionsHeight;
     public final int overviewActionsMarginThreeButtonPx;
     public final int overviewActionsTopMarginGesturePx;
     public final int overviewActionsBottomMarginGesturePx;
@@ -202,8 +205,13 @@
 
     // Drop Target
     public int dropTargetBarSizePx;
+    public int dropTargetBarTopMarginPx;
+    public int dropTargetBarBottomMarginPx;
     public int dropTargetDragPaddingPx;
     public int dropTargetTextSizePx;
+    public int dropTargetHorizontalPaddingPx;
+    public int dropTargetVerticalPaddingPx;
+    public int dropTargetGapPx;
 
     // Insets
     private final Rect mInsets = new Rect();
@@ -229,20 +237,23 @@
     /** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
     DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
             boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
-            boolean useTwoPanels) {
+            boolean useTwoPanels, boolean isGestureMode) {
 
         this.inv = inv;
         this.isLandscape = windowBounds.isLandscape();
         this.isMultiWindowMode = isMultiWindowMode;
         this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
+        this.isGestureMode = isGestureMode;
         windowX = windowBounds.bounds.left;
         windowY = windowBounds.bounds.top;
+        this.rotationHint = windowBounds.rotationHint;
 
         isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
 
         // Determine device posture.
         mInfo = info;
         isTablet = info.isTablet(windowBounds);
+        isLargeTablet = info.isLargeTablet(windowBounds);
         isPhone = !isTablet;
         isTwoPanels = isTablet && useTwoPanels;
         isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS;
@@ -250,7 +261,8 @@
         // Some more constants.
         context = getContext(context, info, isVerticalBarLayout() || (isTablet && isLandscape)
                 ? Configuration.ORIENTATION_LANDSCAPE
-                : Configuration.ORIENTATION_PORTRAIT);
+                : Configuration.ORIENTATION_PORTRAIT,
+                windowBounds);
         final Resources res = context.getResources();
         mMetrics = res.getDisplayMetrics();
 
@@ -288,8 +300,11 @@
         desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
         desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
 
-        allAppsTopPadding = res.getDimensionPixelSize(R.dimen.all_apps_top_padding)
-                + (isTablet ? heightPx - availableHeightPx : 0);
+        bottomSheetTopPadding = windowBounds.insets.top // statusbar height
+                + res.getDimensionPixelSize(R.dimen.bottom_sheet_extra_top_padding)
+                + (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
+
+        allAppsTopPadding = isTablet ? bottomSheetTopPadding : 0;
         allAppsShiftRange = isTablet
                 ? heightPx - allAppsTopPadding
                 : res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
@@ -308,23 +323,6 @@
         folderCellLayoutBorderSpacePx = new Point(folderCellLayoutBorderSpaceOriginalPx,
                 folderCellLayoutBorderSpaceOriginalPx);
 
-        int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet
-                ? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;
-        int cellLayoutPadding = isScalableGrid
-                ? 0
-                : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
-
-        if (isTwoPanels) {
-            cellLayoutPaddingLeftRightPx = 0;
-            cellLayoutBottomPaddingPx = 0;
-        } else if (isLandscape) {
-            cellLayoutPaddingLeftRightPx = 0;
-            cellLayoutBottomPaddingPx = cellLayoutPadding;
-        } else {
-            cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier * cellLayoutPadding;
-            cellLayoutBottomPaddingPx = 0;
-        }
-
         workspacePageIndicatorHeight = res.getDimensionPixelSize(
                 R.dimen.workspace_page_indicator_height);
         mWorkspacePageIndicatorOverlapWorkspace =
@@ -334,8 +332,15 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
 
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
+        dropTargetBarTopMarginPx = res.getDimensionPixelSize(R.dimen.drop_target_top_margin);
+        dropTargetBarBottomMarginPx = res.getDimensionPixelSize(R.dimen.drop_target_bottom_margin);
         dropTargetDragPaddingPx = res.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
         dropTargetTextSizePx = res.getDimensionPixelSize(R.dimen.drop_target_text_size);
+        dropTargetHorizontalPaddingPx = res.getDimensionPixelSize(
+                R.dimen.drop_target_button_drawable_horizontal_padding);
+        dropTargetVerticalPaddingPx = res.getDimensionPixelSize(
+                R.dimen.drop_target_button_drawable_vertical_padding);
+        dropTargetGapPx = res.getDimensionPixelSize(R.dimen.drop_target_button_gap);
 
         workspaceSpringLoadedBottomSpace =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
@@ -343,9 +348,20 @@
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
         hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
-        isQsbInline = isTablet && isLandscape && !isTwoPanels && hotseatQsbHeight > 0;
-        numShownHotseatIcons =
-                isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+        // Whether QSB might be inline in appropriate orientation (landscape).
+        boolean canQsbInline = isLargeTablet && hotseatQsbHeight > 0;
+        isQsbInline = canQsbInline && isLandscape;
+
+        // We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB
+        // might be inline in either orientations, to keep hotseat size consistent across rotation.
+        boolean areNavButtonsInline = isTaskbarPresent && !isGestureMode;
+        if (areNavButtonsInline && canQsbInline) {
+            numShownHotseatIcons = inv.numShrunkenHotseatIcons;
+        } else {
+            numShownHotseatIcons =
+                    isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+        }
+
         numShownAllAppsColumns =
                 isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
         hotseatBarSizeExtraSpacePx = 0;
@@ -362,6 +378,8 @@
                     + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
             qsbWidth = 0;
         }
+        springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
+                R.dimen.spring_loaded_hotseat_top_margin);
         hotseatBarSidePaddingEndPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         // Add a bit of space between nav bar and hotseat in vertical bar layout.
@@ -391,6 +409,7 @@
         overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
         overviewActionsButtonSpacing = res.getDimensionPixelSize(
                 R.dimen.overview_actions_button_spacing);
+        overviewActionsHeight = res.getDimensionPixelSize(R.dimen.overview_actions_height);
         overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
                 R.dimen.overview_actions_margin_three_button);
         // Grid task's top margin is only overviewTaskIconSizePx + overviewTaskMarginGridPx, but
@@ -454,6 +473,12 @@
             // Recalculate the available dimensions using the new hotseat size.
             updateAvailableDimensions(res);
         }
+
+        int cellLayoutPadding =
+                isTwoPanels ? cellLayoutBorderSpacePx.x / 2 : res.getDimensionPixelSize(
+                        R.dimen.cell_layout_padding);
+        cellLayoutPaddingPx = new Rect(cellLayoutPadding, cellLayoutPadding, cellLayoutPadding,
+                cellLayoutPadding);
         updateWorkspacePadding();
 
         flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
@@ -467,8 +492,10 @@
     }
 
     private int calculateQsbWidth() {
-        return cellWidthPx * inv.numColumns
-                + cellLayoutBorderSpacePx.x * (inv.numColumns - 1)
+        int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+
+        return cellWidthPx * columns
+                + cellLayoutBorderSpacePx.x * (columns - 1)
                 - (cellWidthPx - iconSizePx) // left and right cell space
                 - iconSizePx * numShownHotseatIcons
                 - hotseatBorderSpace * numShownHotseatIcons;
@@ -534,13 +561,14 @@
     }
 
     public Builder toBuilder(Context context) {
-        WindowBounds bounds =
-                new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx);
+        WindowBounds bounds = new WindowBounds(
+                widthPx, heightPx, availableWidthPx, availableHeightPx, rotationHint);
         bounds.bounds.offsetTo(windowX, windowY);
         return new Builder(context, inv, mInfo)
                 .setWindowBounds(bounds)
                 .setUseTwoPanels(isTwoPanels)
-                .setMultiWindowMode(isMultiWindowMode);
+                .setMultiWindowMode(isMultiWindowMode)
+                .setGestureMode(isGestureMode);
     }
 
     public DeviceProfile copy(Context context) {
@@ -563,7 +591,6 @@
         float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
         float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y;
         profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY);
-        profile.updateWorkspacePadding();
 
         return profile;
     }
@@ -597,19 +624,20 @@
                 + textHeight + (topBottomPadding * 2);
     }
 
-    private void updateAllAppsWidth(Resources res) {
-
+    private void updateAllAppsContainerWidth(Resources res) {
+        int cellLayoutHorizontalPadding =
+                (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2;
         if (isTablet) {
             allAppsLeftRightPadding =
-                    res.getDimensionPixelSize(R.dimen.all_apps_bottom_sheet_horizontal_padding)
-                            + cellLayoutPaddingLeftRightPx;
+                    res.getDimensionPixelSize(R.dimen.all_apps_bottom_sheet_horizontal_padding);
+
             int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
-                    + (allAppsBorderSpacePx.x * (numShownAllAppsColumns + 1))
+                    + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1))
                     + allAppsLeftRightPadding * 2;
             allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2);
         } else {
             allAppsLeftRightPadding =
-                    desiredWorkspaceHorizontalMarginPx + cellLayoutPaddingLeftRightPx;
+                    desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding;
         }
     }
 
@@ -619,11 +647,12 @@
     private int updateAvailableDimensions(Resources res) {
         updateIconSize(1f, res);
 
+        updateWorkspacePadding();
         Point workspacePadding = getTotalWorkspacePadding();
 
         // Check to see if the icons fit within the available height.
         float usedHeight = getCellLayoutHeight();
-        final int maxHeight = availableHeightPx - workspacePadding.y;
+        final int maxHeight = getWorkspaceHeight(workspacePadding);
         float extraHeight = Math.max(0, maxHeight - usedHeight);
         float scaleY = maxHeight / usedHeight;
         boolean shouldScale = scaleY < 1f;
@@ -633,10 +662,7 @@
             // We scale to fit the cellWidth and cellHeight in the available space.
             // The benefit of scalable grids is that we can get consistent aspect ratios between
             // devices.
-            int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
-            float usedWidth = (cellWidthPx * numColumns)
-                    + (cellLayoutBorderSpacePx.x * (numColumns - 1))
-                    + (desiredWorkspaceHorizontalMarginPx * 2);
+            float usedWidth = getCellLayoutWidth() + (desiredWorkspaceHorizontalMarginPx * 2);
             // We do not subtract padding here, as we also scale the workspace padding if needed.
             scaleX = availableWidthPx / usedWidth;
             shouldScale = true;
@@ -653,7 +679,14 @@
     }
 
     private int getCellLayoutHeight() {
-        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacePx.y * (inv.numRows - 1));
+        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacePx.y * (inv.numRows - 1))
+                + cellLayoutPaddingPx.top + cellLayoutPaddingPx.bottom;
+    }
+
+    private int getCellLayoutWidth() {
+        int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+        return (cellWidthPx * numColumns) + (cellLayoutBorderSpacePx.x * (numColumns - 1))
+                + cellLayoutPaddingPx.left + cellLayoutPaddingPx.right;
     }
 
     /**
@@ -713,18 +746,6 @@
         }
         updateHotseatIconSize(iconSizePx);
 
-        if (!isVerticalLayout) {
-            int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
-                    - workspacePageIndicatorHeight - edgeMarginPx;
-            float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
-            workspaceSpringLoadShrinkFactor = Math.min(
-                    res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
-                    1 - (minRequiredHeight / expectedWorkspaceHeight));
-        } else {
-            workspaceSpringLoadShrinkFactor =
-                    res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
-        }
-
         // Folder icon
         folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
         folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
@@ -735,24 +756,26 @@
      * Updates the iconSize for allApps* variants.
      */
     public void updateAllAppsIconSize(float scale, Resources res) {
-        if (numShownAllAppsColumns != inv.numColumns) {
+        //TODO(b/218638090): remove the tablet condition once we have phone specs
+        if (isScalableGrid && isTablet) {
             allAppsIconSizePx =
                     pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
             allAppsIconTextSizePx =
                     pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
-            autoResizeAllAppsCells();
+            allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
+            allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics, scale);
         } else {
             float invIconSizeDp = inv.iconSize[mTypeIndex];
             float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
             allAppsIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
             allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale);
             allAppsIconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
+            allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
             allAppsCellHeightPx = getCellSize().y;
         }
 
-        allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
-        updateAllAppsWidth(res);
+        updateAllAppsContainerWidth(res);
         if (isVerticalBarLayout()) {
             hideWorkspaceLabelsIfNotEnoughSpace();
         }
@@ -825,7 +848,6 @@
 
     public void updateInsets(Rect insets) {
         mInsets.set(insets);
-        updateWorkspacePadding();
     }
 
     /**
@@ -852,23 +874,62 @@
         int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
         int screenWidthPx = getWorkspaceWidth(padding);
         result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacePx.x, numColumns);
-        result.y = calculateCellHeight(availableHeightPx - padding.y
-                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacePx.y, inv.numRows);
+        int screenHeightPx = getWorkspaceHeight(padding);
+        result.y = calculateCellHeight(screenHeightPx, cellLayoutBorderSpacePx.y, inv.numRows);
         return result;
     }
 
+    /**
+     * Gets the space in px from the bottom of last item in the vertical-bar hotseat to the
+     * bottom of the screen.
+     */
+    public int getVerticalHotseatLastItemBottomOffset() {
+        int cellHeight = calculateCellHeight(
+                heightPx - mHotseatPadding.top - mHotseatPadding.bottom, hotseatBorderSpace,
+                numShownHotseatIcons);
+        int hotseatSize = (cellHeight * numShownHotseatIcons)
+                + (hotseatBorderSpace * (numShownHotseatIcons - 1));
+        int extraHotseatEndSpacing = (heightPx - hotseatSize) / 2;
+        int extraIconEndSpacing = (cellHeight - iconSizePx) / 2;
+        return extraHotseatEndSpacing + extraIconEndSpacing + mHotseatPadding.bottom;
+    }
+
+    /**
+     * Gets the scaled top of the workspace in px for the spring-loaded edit state.
+     */
+    public float getWorkspaceSpringLoadShrunkTop() {
+        workspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
+                + dropTargetBarBottomMarginPx;
+        return workspaceSpringLoadShrunkTop;
+    }
+
+    /**
+     * Gets the scaled bottom of the workspace in px for the spring-loaded edit state.
+     */
+    public float getWorkspaceSpringLoadShrunkBottom() {
+        int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx;
+        workspaceSpringLoadShrunkBottom =
+                heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset()
+                        : topOfHotseat);
+        return workspaceSpringLoadShrunkBottom;
+    }
+
     public int getWorkspaceWidth() {
         return getWorkspaceWidth(getTotalWorkspacePadding());
     }
 
     public int getWorkspaceWidth(Point workspacePadding) {
         int cellLayoutTotalPadding =
-                isTwoPanels ? 4 * cellLayoutPaddingLeftRightPx : 2 * cellLayoutPaddingLeftRightPx;
+                (isTwoPanels ? 2 : 1) * (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right);
         return availableWidthPx - workspacePadding.x - cellLayoutTotalPadding;
     }
 
+    private int getWorkspaceHeight(Point workspacePadding) {
+        return availableHeightPx - workspacePadding.y - (cellLayoutPaddingPx.top
+                + cellLayoutPaddingPx.bottom);
+    }
+
     public Point getTotalWorkspacePadding() {
-        updateWorkspacePadding();
         return new Point(workspacePadding.left + workspacePadding.right,
                 workspacePadding.top + workspacePadding.bottom);
     }
@@ -894,12 +955,26 @@
             int hotseatTop = hotseatBarSizePx;
             int paddingBottom = hotseatTop + workspacePageIndicatorHeight
                     + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
+            int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx);
+            int paddingSide = desiredWorkspaceHorizontalMarginPx;
 
-            padding.set(desiredWorkspaceHorizontalMarginPx,
-                    workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx),
-                    desiredWorkspaceHorizontalMarginPx,
-                    paddingBottom);
+            padding.set(paddingSide, paddingTop, paddingSide, paddingBottom);
         }
+        insetPadding(workspacePadding, cellLayoutPaddingPx);
+    }
+
+    private void insetPadding(Rect paddings, Rect insets) {
+        insets.left = Math.min(insets.left, paddings.left);
+        paddings.left -= insets.left;
+
+        insets.top = Math.min(insets.top, paddings.top);
+        paddings.top -= insets.top;
+
+        insets.right = Math.min(insets.right, paddings.right);
+        paddings.right -= insets.right;
+
+        insets.bottom = Math.min(insets.bottom, paddings.bottom);
+        paddings.bottom -= insets.bottom;
     }
 
     /**
@@ -907,12 +982,14 @@
      */
     public Rect getHotseatLayoutPadding(Context context) {
         if (isVerticalBarLayout()) {
+            int paddingTop = Math.max(mInsets.top - cellLayoutPaddingPx.top, 0);
+            int paddingBottom = Math.max(mInsets.bottom - cellLayoutPaddingPx.bottom, 0);
             if (isSeascape()) {
-                mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx,
-                        mInsets.top, hotseatBarSidePaddingEndPx, mInsets.bottom);
+                mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
+                        hotseatBarSidePaddingEndPx, paddingBottom);
             } else {
-                mHotseatPadding.set(hotseatBarSidePaddingEndPx, mInsets.top,
-                        mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom);
+                mHotseatPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
+                        mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
             }
         } else if (isTaskbarPresent) {
             int hotseatHeight = workspacePadding.bottom;
@@ -924,7 +1001,7 @@
                 additionalLeftSpace = qsbWidth + hotseatBorderSpace;
             }
 
-            int hotseatTopDiff = hotseatHeight - taskbarOffset;
+            int hotseatTopPadding = hotseatHeight - taskbarOffset - hotseatCellHeightPx;
 
             int endOffset = ApiWrapper.getHotseatEndOffset(context);
             int requiredWidth = iconSizePx * numShownHotseatIcons
@@ -933,7 +1010,7 @@
 
             int hotseatSize = Math.min(requiredWidth, availableWidthPx - endOffset);
             int sideSpacing = (availableWidthPx - hotseatSize) / 2;
-            mHotseatPadding.set(sideSpacing + additionalLeftSpace, hotseatTopDiff, sideSpacing,
+            mHotseatPadding.set(sideSpacing + additionalLeftSpace, hotseatTopPadding, sideSpacing,
                     taskbarOffset);
 
             if (endOffset > sideSpacing) {
@@ -951,14 +1028,12 @@
             float workspaceCellWidth = (float) widthPx / inv.numColumns;
             float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
             int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
-            mHotseatPadding.set(
-                    hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
-                            + mInsets.left,
-                    hotseatBarTopPaddingPx,
-                    hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx
+            mHotseatPadding.set(hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
+                            + mInsets.left, hotseatBarTopPaddingPx,
+                    hotseatAdjustment + workspacePadding.right + cellLayoutPaddingPx.right
                             + mInsets.right,
                     hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
-                            + cellLayoutBottomPaddingPx + mInsets.bottom);
+                            + mInsets.bottom);
         }
         return mHotseatPadding;
     }
@@ -1042,6 +1117,8 @@
                     .getInfo().rotation == Surface.ROTATION_270;
             if (mIsSeascape != isSeascape) {
                 mIsSeascape = isSeascape;
+                // Hotseat changing sides requires updating workspace left/right paddings
+                updateWorkspacePadding();
                 return true;
             }
         }
@@ -1081,9 +1158,11 @@
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
 
         writer.println(prefix + "\tisTablet:" + isTablet);
+        writer.println(prefix + "\tisLargeTablet:" + isLargeTablet);
         writer.println(prefix + "\tisPhone:" + isPhone);
         writer.println(prefix + "\ttransposeLayoutWithOrientation:"
                 + transposeLayoutWithOrientation);
+        writer.println(prefix + "\tisGestureMode:" + isGestureMode);
 
         writer.println(prefix + "\tisLandscape:" + isLandscape);
         writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
@@ -1118,6 +1197,11 @@
                 cellLayoutBorderSpacePx.x));
         writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Vertical",
                 cellLayoutBorderSpacePx.y));
+        writer.println(prefix + pxToDpStr("cellLayoutPaddingPx.left", cellLayoutPaddingPx.left));
+        writer.println(prefix + pxToDpStr("cellLayoutPaddingPx.top", cellLayoutPaddingPx.top));
+        writer.println(prefix + pxToDpStr("cellLayoutPaddingPx.right", cellLayoutPaddingPx.right));
+        writer.println(
+                prefix + pxToDpStr("cellLayoutPaddingPx.bottom", cellLayoutPaddingPx.bottom));
 
         writer.println(prefix + pxToDpStr("iconSizePx", iconSizePx));
         writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx));
@@ -1142,6 +1226,7 @@
                 allAppsIconDrawablePaddingPx));
         writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
         writer.println(prefix + pxToDpStr("allAppsCellWidthPx", allAppsCellWidthPx));
+        writer.println(prefix + pxToDpStr("allAppsBorderSpacePx", allAppsBorderSpacePx.x));
         writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
         writer.println(prefix + pxToDpStr("allAppsLeftRightPadding", allAppsLeftRightPadding));
         writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin));
@@ -1154,6 +1239,12 @@
                 hotseatBarSidePaddingStartPx));
         writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
                 hotseatBarSidePaddingEndPx));
+        writer.println(prefix + pxToDpStr("springLoadedHotseatBarTopMarginPx",
+                springLoadedHotseatBarTopMarginPx));
+        writer.println(prefix + pxToDpStr("mHotseatPadding.top", mHotseatPadding.top));
+        writer.println(prefix + pxToDpStr("mHotseatPadding.bottom", mHotseatPadding.bottom));
+        writer.println(prefix + pxToDpStr("mHotseatPadding.left", mHotseatPadding.left));
+        writer.println(prefix + pxToDpStr("mHotseatPadding.right", mHotseatPadding.right));
         writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
         writer.println(prefix + pxToDpStr("hotseatBorderSpace", hotseatBorderSpace));
         writer.println(prefix + "\tisQsbInline: " + isQsbInline);
@@ -1204,12 +1295,23 @@
         writer.println(prefix + pxToDpStr("overviewPageSpacing", overviewPageSpacing));
         writer.println(prefix + pxToDpStr("overviewRowSpacing", overviewRowSpacing));
         writer.println(prefix + pxToDpStr("overviewGridSideMargin", overviewGridSideMargin));
+
+        writer.println(prefix + pxToDpStr("dropTargetBarTopMarginPx", dropTargetBarTopMarginPx));
+        writer.println(prefix + pxToDpStr("dropTargetBarSizePx", dropTargetBarSizePx));
+        writer.println(
+                prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx));
+
+        writer.println(
+                prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
+        writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
+                workspaceSpringLoadShrunkBottom));
     }
 
-    private static Context getContext(Context c, Info info, int orientation) {
+    private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) {
         Configuration config = new Configuration(c.getResources().getConfiguration());
         config.orientation = orientation;
         config.densityDpi = info.densityDpi;
+        config.smallestScreenWidthDp = (int) info.smallestSizeDp(bounds);
         return c.createConfigurationContext(config);
     }
 
@@ -1266,6 +1368,7 @@
 
         private boolean mIsMultiWindowMode = false;
         private Boolean mTransposeLayoutWithOrientation;
+        private Boolean mIsGestureMode;
 
         public Builder(Context context, InvariantDeviceProfile inv, Info info) {
             mContext = context;
@@ -1294,6 +1397,11 @@
             return this;
         }
 
+        public Builder setGestureMode(boolean isGestureMode) {
+            mIsGestureMode = isGestureMode;
+            return this;
+        }
+
         public DeviceProfile build() {
             if (mWindowBounds == null) {
                 throw new IllegalArgumentException("Window bounds not set");
@@ -1301,8 +1409,11 @@
             if (mTransposeLayoutWithOrientation == null) {
                 mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
             }
-            return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds,
-                    mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels);
+            if (mIsGestureMode == null) {
+                mIsGestureMode = DisplayController.getNavigationMode(mContext).hasGestures;
+            }
+            return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mIsMultiWindowMode,
+                    mTransposeLayoutWithOrientation, mUseTwoPanels, mIsGestureMode);
         }
     }
 
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 9fb14f6..73289fb 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -17,8 +17,6 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
-import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
-import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
 import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
 
 import android.animation.TimeInterpolator;
@@ -41,6 +39,8 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.testing.TestProtocol;
 
+import java.util.Arrays;
+
 /*
  * The top bar containing various drop targets: Delete/App Info/Uninstall.
  */
@@ -94,30 +94,28 @@
         lp.rightMargin = insets.right;
         int tooltipLocation = TOOLTIP_DEFAULT;
 
-        if (grid.isVerticalBarLayout()) {
-            lp.width = grid.dropTargetBarSizePx;
-            lp.height = grid.availableHeightPx - 2 * grid.edgeMarginPx;
-            lp.gravity = grid.isSeascape() ? Gravity.RIGHT : Gravity.LEFT;
-            tooltipLocation = grid.isSeascape() ? TOOLTIP_LEFT : TOOLTIP_RIGHT;
+        int horizontalMargin;
+        if (grid.isTablet) {
+            // XXX: If the icon size changes across orientations, we will have to take
+            //      that into account here too.
+            horizontalMargin = ((grid.widthPx - 2 * grid.edgeMarginPx
+                    - (grid.inv.numColumns * grid.cellWidthPx))
+                    / (2 * (grid.inv.numColumns + 1)))
+                    + grid.edgeMarginPx;
         } else {
-            int gap;
-            if (grid.isTablet) {
-                // XXX: If the icon size changes across orientations, we will have to take
-                //      that into account here too.
-                gap = ((grid.widthPx - 2 * grid.edgeMarginPx
-                        - (grid.inv.numColumns * grid.cellWidthPx))
-                        / (2 * (grid.inv.numColumns + 1)))
-                        + grid.edgeMarginPx;
-            } else {
-                gap = getContext().getResources()
-                        .getDimensionPixelSize(R.dimen.drop_target_bar_margin_horizontal);
-            }
-            lp.width = grid.availableWidthPx - 2 * gap;
-
-            lp.topMargin += grid.edgeMarginPx;
-            lp.height = grid.dropTargetBarSizePx;
-            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+            horizontalMargin = getContext().getResources()
+                    .getDimensionPixelSize(R.dimen.drop_target_bar_margin_horizontal);
         }
+        lp.topMargin += grid.dropTargetBarTopMarginPx;
+        lp.bottomMargin += grid.dropTargetBarBottomMarginPx;
+        lp.width = grid.availableWidthPx - 2 * horizontalMargin;
+        if (mIsVertical) {
+            lp.leftMargin = (grid.widthPx - lp.width) / 2;
+            lp.rightMargin = (grid.widthPx - lp.width) / 2;
+        }
+        lp.height = grid.dropTargetBarSizePx;
+        lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+
         setLayoutParams(lp);
         for (ButtonDropTarget button : mDropTargets) {
             button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx);
@@ -139,19 +137,7 @@
         int height = MeasureSpec.getSize(heightMeasureSpec);
 
         int visibleCount = getVisibleButtonsCount();
-        if (visibleCount == 0) {
-            // do nothing
-        } else if (mIsVertical) {
-            int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
-            int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
-
-            for (ButtonDropTarget button : mDropTargets) {
-                if (button.getVisibility() != GONE) {
-                    button.setTextVisible(false);
-                    button.measure(widthSpec, heightSpec);
-                }
-            }
-        } else {
+        if (visibleCount > 0) {
             int availableWidth = width / visibleCount;
             boolean textVisible = true;
             for (ButtonDropTarget buttons : mDropTargets) {
@@ -176,31 +162,91 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         int visibleCount = getVisibleButtonsCount();
         if (visibleCount == 0) {
-            // do nothing
-        } else if (mIsVertical) {
-            int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
-            int start = gap;
-            int end;
+            return;
+        }
 
-            for (ButtonDropTarget button : mDropTargets) {
-                if (button.getVisibility() != GONE) {
-                    end = start + button.getMeasuredHeight();
-                    button.layout(0, start, button.getMeasuredWidth(), end);
-                    start = end + gap;
-                }
-            }
-        } else {
-            int frameSize = (right - left) / visibleCount;
+        Launcher launcher = Launcher.getLauncher(getContext());
+        Workspace workspace = launcher.getWorkspace();
+        DeviceProfile dp = launcher.getDeviceProfile();
+        int buttonHorizontalPadding = dp.dropTargetHorizontalPaddingPx;
+        int buttonVerticalPadding = dp.dropTargetVerticalPaddingPx;
+        int barCenter = (right - left) / 2;
 
-            int start = frameSize / 2;
-            int halfWidth;
-            for (ButtonDropTarget button : mDropTargets) {
-                if (button.getVisibility() != GONE) {
-                    halfWidth = button.getMeasuredWidth() / 2;
-                    button.layout(start - halfWidth, 0,
-                            start + halfWidth, button.getMeasuredHeight());
-                    start = start + frameSize;
+        ButtonDropTarget[] visibleButtons = Arrays.stream(mDropTargets)
+                .filter(b -> b.getVisibility() != GONE)
+                .toArray(ButtonDropTarget[]::new);
+        Arrays.stream(visibleButtons).forEach(
+                b -> b.setPadding(buttonHorizontalPadding, buttonVerticalPadding,
+                        buttonHorizontalPadding, buttonVerticalPadding));
+
+        if (visibleCount == 1) {
+            ButtonDropTarget button = visibleButtons[0];
+            button.layout(barCenter - (button.getMeasuredWidth() / 2), 0,
+                    barCenter + (button.getMeasuredWidth() / 2), button.getMeasuredHeight());
+        } else if (visibleCount == 2) {
+            int buttonGap = dp.dropTargetGapPx;
+
+            if (dp.isTwoPanels) {
+                ButtonDropTarget leftButton = visibleButtons[0];
+                leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
+                        barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
+
+                ButtonDropTarget rightButton = visibleButtons[1];
+                rightButton.layout(barCenter + (buttonGap / 2), 0,
+                        barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
+                        rightButton.getMeasuredHeight());
+            } else if (dp.isTablet) {
+                int numberOfMargins = visibleCount - 1;
+                int buttonWidths = Arrays.stream(mDropTargets)
+                        .filter(b -> b.getVisibility() != GONE)
+                        .mapToInt(ButtonDropTarget::getMeasuredWidth)
+                        .sum();
+                int totalWidth = buttonWidths + (numberOfMargins * buttonGap);
+                int buttonsStartMargin = barCenter - (totalWidth / 2);
+
+                int start = buttonsStartMargin;
+                for (ButtonDropTarget button : visibleButtons) {
+                    int margin = (start != buttonsStartMargin) ? buttonGap : 0;
+                    button.layout(start + margin, 0, start + margin + button.getMeasuredWidth(),
+                            button.getMeasuredHeight());
+                    start += button.getMeasuredWidth() + margin;
                 }
+            } else if (mIsVertical) {
+                // Center buttons over workspace, not screen.
+                int verticalCenter = (workspace.getRight() - workspace.getLeft()) / 2;
+                ButtonDropTarget leftButton = visibleButtons[0];
+                leftButton.layout(verticalCenter - leftButton.getMeasuredWidth() - (buttonGap / 2),
+                        0, verticalCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
+
+                ButtonDropTarget rightButton = visibleButtons[1];
+                rightButton.layout(verticalCenter + (buttonGap / 2), 0,
+                        verticalCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
+                        rightButton.getMeasuredHeight());
+            } else if (dp.isPhone) {
+                // Buttons aligned to outer edges of scaled workspace.
+                float shrunkTop = dp.getWorkspaceSpringLoadShrunkTop();
+                float shrunkBottom = dp.getWorkspaceSpringLoadShrunkBottom();
+                float scale =
+                        (shrunkBottom - shrunkTop) / launcher.getWorkspace().getNormalChildHeight();
+                int workspaceWidth = (int) (launcher.getWorkspace().getNormalChildWidth() * scale);
+                int start = barCenter - (workspaceWidth / 2);
+                int end = barCenter + (workspaceWidth / 2);
+
+                ButtonDropTarget leftButton = visibleButtons[0];
+                ButtonDropTarget rightButton = visibleButtons[1];
+
+                // If the text within the buttons is too long, the buttons can overlap
+                int overlap = start + leftButton.getMeasuredWidth() + rightButton.getMeasuredWidth()
+                        - end;
+                if (overlap > 0) {
+                    start -= overlap / 2;
+                    end += overlap / 2;
+                }
+
+                leftButton.layout(start, 0, start + leftButton.getMeasuredWidth(),
+                        leftButton.getMeasuredHeight());
+                rightButton.layout(end - rightButton.getMeasuredWidth(), 0, end,
+                        rightButton.getMeasuredHeight());
             }
         }
     }
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index a199a57..fdf0101 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index cb0cc11..9c749aa7 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -84,6 +84,7 @@
         removeAllViewsInLayout();
         mHasVerticalHotseat = hasVerticalHotseat;
         DeviceProfile dp = mActivity.getDeviceProfile();
+        resetCellSize(dp);
         if (hasVerticalHotseat) {
             setGridSize(1, dp.numShownHotseatIcons);
         } else {
@@ -110,10 +111,9 @@
             mQsb.setVisibility(View.VISIBLE);
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-            lp.height = (grid.isTaskbarPresent
+            lp.height = grid.isTaskbarPresent
                     ? grid.workspacePadding.bottom
-                        : grid.hotseatBarSizePx)
-                    + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
+                    : grid.hotseatBarSizePx + insets.bottom;
         }
 
         Rect padding = grid.getHotseatLayoutPadding(getContext());
@@ -173,9 +173,15 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        int width = mActivity.getDeviceProfile().isQsbInline
-                ? mActivity.getDeviceProfile().qsbWidth
-                : getShortcutsAndWidgets().getMeasuredWidth();
+        int width;
+        if (mActivity.getDeviceProfile().isQsbInline) {
+            width = mActivity.getDeviceProfile().qsbWidth;
+        } else {
+            MarginLayoutParams qsbParams = (MarginLayoutParams) mQsb.getLayoutParams();
+            width = getShortcutsAndWidgets().getMeasuredWidth()
+                    - qsbParams.getMarginStart()
+                    - qsbParams.getMarginEnd();
+        }
 
         mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index dfe4bb0..219ed9e 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -54,6 +55,7 @@
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.util.window.WindowManagerProxy;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -126,6 +128,7 @@
 
     public float[] horizontalMargin;
 
+    public PointF[] allAppsCellSize;
     public float[] allAppsIconSize;
     public float[] allAppsIconTextSize;
     public PointF[] allAppsBorderSpaces;
@@ -138,6 +141,11 @@
     public int numShownHotseatIcons;
 
     /**
+     * Number of icons inside the hotseat area when using 3 buttons navigation.
+     */
+    public int numShrunkenHotseatIcons;
+
+    /**
      * Number of icons inside the hotseat area that is stored in the database. This is greater than
      * or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat
      * sizes that share the same DB.
@@ -174,8 +182,7 @@
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
 
     @VisibleForTesting
-    public InvariantDeviceProfile() {
-    }
+    public InvariantDeviceProfile() { }
 
     @TargetApi(23)
     private InvariantDeviceProfile(Context context) {
@@ -188,7 +195,8 @@
 
         DisplayController.INSTANCE.get(context).setPriorityListener(
                 (displayContext, info, flags) -> {
-                    if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
+                    if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS
+                            | CHANGE_NAVIGATION_MODE)) != 0) {
                         onConfigChanged(displayContext);
                     }
                 });
@@ -271,11 +279,16 @@
     }
 
     private static @DeviceType int getDeviceType(Info displayInfo) {
-        // Each screen has two profiles (portrait/landscape), so devices with four or more
-        // supported profiles implies two or more internal displays.
-        if (displayInfo.supportedBounds.size() >= 4 && ENABLE_TWO_PANEL_HOME.get()) {
+        int flagPhone = 1 << 0;
+        int flagTablet = 1 << 1;
+
+        int type = displayInfo.supportedBounds.stream()
+                .mapToInt(bounds -> displayInfo.isTablet(bounds) ? flagTablet : flagPhone)
+                .reduce(0, (a, b) -> a | b);
+        if ((type == (flagPhone | flagTablet)) && ENABLE_TWO_PANEL_HOME.get()) {
+            // device has profiles supporting both phone and table modes
             return TYPE_MULTI_DISPLAY;
-        } else if (displayInfo.supportedBounds.stream().allMatch(displayInfo::isTablet)) {
+        } else if (type == flagTablet) {
             return TYPE_TABLET;
         } else {
             return TYPE_PHONE;
@@ -336,6 +349,7 @@
         horizontalMargin = displayOption.horizontalMargin;
 
         numShownHotseatIcons = closestProfile.numHotseatIcons;
+        numShrunkenHotseatIcons = closestProfile.numShrunkenHotseatIcons;
         numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
         hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
@@ -344,6 +358,7 @@
         numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
+        allAppsCellSize = displayOption.allAppsCellSize;
         allAppsBorderSpaces = displayOption.allAppsBorderSpaces;
         allAppsIconSize = displayOption.allAppsIconSizes;
         allAppsIconTextSize = displayOption.allAppsIconTextSizes;
@@ -365,7 +380,8 @@
         for (WindowBounds bounds : displayInfo.supportedBounds) {
             localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
                     .setUseTwoPanels(deviceType == TYPE_MULTI_DISPLAY)
-                    .setWindowBounds(bounds).build());
+                    .setWindowBounds(bounds)
+                    .build());
 
             // Wallpaper size should be the maximum of the all possible sizes Launcher expects
             int displayWidth = bounds.bounds.width();
@@ -604,10 +620,14 @@
 
         float screenWidth = config.screenWidthDp * res.getDisplayMetrics().density;
         float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
-        return getBestMatch(screenWidth, screenHeight);
+        return getBestMatch(screenWidth, screenHeight,
+                WindowManagerProxy.INSTANCE.get(context).getRotation(context));
     }
 
-    public DeviceProfile getBestMatch(float screenWidth, float screenHeight) {
+    /**
+     * Returns the device profile matching the provided screen configuration
+     */
+    public DeviceProfile getBestMatch(float screenWidth, float screenHeight, int rotation) {
         DeviceProfile bestMatch = supportedProfiles.get(0);
         float minDiff = Float.MAX_VALUE;
 
@@ -617,6 +637,8 @@
             if (diff < minDiff) {
                 minDiff = diff;
                 bestMatch = profile;
+            } else if (diff == minDiff && profile.rotationHint == rotation) {
+                bestMatch = profile;
             }
         }
         return bestMatch;
@@ -691,6 +713,7 @@
         private final int numAllAppsColumns;
         private final int numDatabaseAllAppsColumns;
         private final int numHotseatIcons;
+        private final int numShrunkenHotseatIcons;
         private final int numDatabaseHotseatIcons;
 
         private final String dbFile;
@@ -727,6 +750,8 @@
 
             numHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
+            numShrunkenHotseatIcons = a.getInt(
+                    R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2);
             numDatabaseHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
 
@@ -775,6 +800,7 @@
         private final float[] iconSizes = new float[COUNT_SIZES];
         private final float[] textSizes = new float[COUNT_SIZES];
 
+        private final PointF[] allAppsCellSize = new PointF[COUNT_SIZES];
         private final float[] allAppsIconSizes = new float[COUNT_SIZES];
         private final float[] allAppsIconTextSizes = new float[COUNT_SIZES];
         private final PointF[] allAppsBorderSpaces = new PointF[COUNT_SIZES];
@@ -815,6 +841,8 @@
             minCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
 
             float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpace, 0);
+            float borderSpaceLandscape = a.getFloat(
+                    R.styleable.ProfileDisplayOption_borderSpaceLandscape, borderSpace);
             float borderSpaceTwoPanelPortrait = a.getFloat(
                     R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortrait, borderSpace);
             float borderSpaceTwoPanelLandscape = a.getFloat(
@@ -823,6 +851,11 @@
             x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontal, borderSpace);
             y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVertical, borderSpace);
             borderSpaces[INDEX_DEFAULT] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceLandscapeHorizontal,
+                    borderSpaceLandscape);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceLandscapeVertical,
+                    borderSpaceLandscape);
             borderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
 
             x = a.getFloat(
@@ -843,9 +876,35 @@
 
             folderBorderSpace = borderSpace;
 
+            x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidth,
+                    minCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeight,
+                    minCellSize[INDEX_DEFAULT].y);
+            allAppsCellSize[INDEX_DEFAULT] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthLandscape,
+                    allAppsCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightLandscape,
+                    allAppsCellSize[INDEX_DEFAULT].y);
+            allAppsCellSize[INDEX_LANDSCAPE] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthTwoPanelPortrait,
+                    allAppsCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightTwoPanelPortrait,
+                    allAppsCellSize[INDEX_DEFAULT].y);
+            allAppsCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthTwoPanelLandscape,
+                    allAppsCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightTwoPanelLandscape,
+                    allAppsCellSize[INDEX_DEFAULT].y);
+            allAppsCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
+
             x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpace,
                     borderSpace);
             allAppsBorderSpaces[INDEX_DEFAULT] = new PointF(x, y);
+            x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpaceLandscape,
+                    allAppsBorderSpaces[INDEX_DEFAULT].x);
             allAppsBorderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
             x = y = a.getFloat(
                     R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortrait,
@@ -941,6 +1000,7 @@
                 textSizes[i] = 0;
                 borderSpaces[i] = new PointF();
                 minCellSize[i] = new PointF();
+                allAppsCellSize[i] = new PointF();
                 allAppsIconSizes[i] = 0;
                 allAppsIconTextSizes[i] = 0;
                 allAppsBorderSpaces[i] = new PointF();
@@ -957,6 +1017,8 @@
                 minCellSize[i].y *= w;
                 horizontalMargin[i] *= w;
                 hotseatBorderSpaces[i] *= w;
+                allAppsCellSize[i].x *= w;
+                allAppsCellSize[i].y *= w;
                 allAppsIconSizes[i] *= w;
                 allAppsIconTextSizes[i] *= w;
                 allAppsBorderSpaces[i].x *= w;
@@ -978,6 +1040,8 @@
                 minCellSize[i].y += p.minCellSize[i].y;
                 horizontalMargin[i] += p.horizontalMargin[i];
                 hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
+                allAppsCellSize[i].x += p.allAppsCellSize[i].x;
+                allAppsCellSize[i].y += p.allAppsCellSize[i].y;
                 allAppsIconSizes[i] += p.allAppsIconSizes[i];
                 allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i];
                 allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8fb2f53..2f9b563 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -57,6 +57,7 @@
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
 
 import android.animation.Animator;
@@ -228,6 +229,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
@@ -500,6 +502,8 @@
 
         if (!mModel.addCallbacksAndLoad(this)) {
             if (!internalStateHandled) {
+                Log.d(BAD_STATE, "Launcher onCreate not binding sync, setting DragLayer alpha "
+                        + "ALPHA_INDEX_LAUNCHER_LOAD to 0");
                 // If we are not binding synchronously, show a fade in animation when
                 // the first page bind completes.
                 mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
@@ -518,6 +522,8 @@
                         final AlphaProperty property =
                                 mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
                         if (property.getValue() == 0) {
+                            Log.d(BAD_STATE, "Launcher onPreDraw ALPHA_INDEX_LAUNCHER_LOAD not"
+                                    + " started yet, cancelling draw.");
                             // Animation haven't started yet; suspend.
                             return false;
                         } else {
@@ -1108,21 +1114,23 @@
                 && mAllAppsSessionLogId == null) {
             // creates new instance ID since new all apps session is started.
             mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId();
-            getStatsLogManager().logger().withContainerInfo(
-                    ContainerInfo.newBuilder().setWorkspace(
-                            WorkspaceContainer.newBuilder().setPageIndex(
-                                    getWorkspace().getCurrentPage())).build())
-                    .log(getAllAppsEntryEvent());
+            if (getAllAppsEntryEvent().isPresent()) {
+                getStatsLogManager().logger()
+                        .withContainerInfo(ContainerInfo.newBuilder()
+                                .setWorkspace(WorkspaceContainer.newBuilder()
+                                        .setPageIndex(getWorkspace().getCurrentPage())).build())
+                        .log(getAllAppsEntryEvent().get());
+            }
         }
     }
 
     /**
      * Returns {@link EventEnum} that should be logged when Launcher enters into AllApps state.
      */
-    protected EventEnum getAllAppsEntryEvent() {
-        return FeatureFlags.ENABLE_DEVICE_SEARCH.get()
+    protected Optional<EventEnum> getAllAppsEntryEvent() {
+        return Optional.of(FeatureFlags.ENABLE_DEVICE_SEARCH.get()
                 ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
-                : LAUNCHER_ALLAPPS_ENTRY;
+                : LAUNCHER_ALLAPPS_ENTRY);
     }
 
     @Override
@@ -1151,17 +1159,18 @@
                 // Making sure mAllAppsSessionLogId is not null to avoid double logging.
                 && mAllAppsSessionLogId != null) {
             getAppsView().reset(false);
-            getStatsLogManager().logger()
-                    .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
-                            .setWorkspace(
-                                    LauncherAtom.WorkspaceContainer.newBuilder()
-                                            .setPageIndex(getWorkspace().getCurrentPage()))
-                            .build())
-                    .log(LAUNCHER_ALLAPPS_EXIT);
+            getAllAppsExitEvent().ifPresent(getStatsLogManager().logger()::log);
             mAllAppsSessionLogId = null;
         }
     }
 
+    /**
+     * Returns {@link EventEnum} that should be logged when Launcher exists from AllApps state.
+     */
+    protected Optional<EventEnum> getAllAppsExitEvent() {
+        return Optional.of(LAUNCHER_ALLAPPS_EXIT);
+    }
+
     @Override
     protected void onResume() {
         Object traceToken = TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT,
@@ -2056,7 +2065,7 @@
 
     @TargetApi(Build.VERSION_CODES.M)
     @Override
-    protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+    public boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
         // Due to legacy reasons, direct call shortcuts require Launchers to have the
         // corresponding permission. Show the appropriate permission prompt if that
         // is the case.
@@ -2681,6 +2690,28 @@
         AlphaProperty property = mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD);
         if (property.getValue() < 1) {
             ObjectAnimator anim = ObjectAnimator.ofFloat(property, MultiValueAlpha.VALUE, 1);
+
+            Log.d(BAD_STATE, "Launcher onInitialBindComplete toAlpha=" + 1);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    Log.d(BAD_STATE, "Launcher onInitialBindComplete onStart");
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    float alpha = mDragLayer == null
+                            ? -1
+                            : mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).getValue();
+                    Log.d(BAD_STATE, "Launcher onInitialBindComplete onCancel, alpha=" + alpha);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    Log.d(BAD_STATE, "Launcher onInitialBindComplete onEnd");
+                }
+            });
+
             anim.addListener(AnimatorListeners.forEndCallback(executor::onLoadAnimationCompleted));
             anim.start();
         } else {
@@ -2743,8 +2774,11 @@
      * @param preferredItemId The id of the preferred item to match to if it exists.
      * @param packageName The package name of the app to match.
      * @param user The user of the app to match.
+     * @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps.
+     *                             Else we only looks on the workspace.
      */
-    public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
+    public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user,
+            boolean supportsAllAppsState) {
         final ItemInfoMatcher preferredItem = (info, cn) ->
                 info != null && info.id == preferredItemId;
         final ItemInfoMatcher packageAndUserAndApp = (info, cn) ->
@@ -2755,7 +2789,7 @@
                         && TextUtils.equals(info.getTargetComponent().getPackageName(),
                         packageName);
 
-        if (isInState(LauncherState.ALL_APPS)) {
+        if (supportsAllAppsState && isInState(LauncherState.ALL_APPS)) {
             return getFirstMatch(Collections.singletonList(mAppsView.getActiveRecyclerView()),
                     preferredItem, packageAndUserAndApp);
         } else {
@@ -3192,4 +3226,15 @@
     public ArrowPopup<?> getOptionsPopup() {
         return findViewById(R.id.popup_container);
     }
+
+    /** Pauses view updates that should not be run during the app launch animation. */
+    public void pauseExpensiveViewUpdates() {
+        // Pause page indicator animations as they lead to layer trashing.
+        getWorkspace().getPageIndicator().pauseAnimations();
+    }
+
+    /** Resumes view updates at the end of the app launch animation. */
+    public void resumeExpensiveViewUpdates() {
+        getWorkspace().getPageIndicator().skipAnimationsToEnd();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e004a4b..5aa8a46 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -102,6 +102,8 @@
     public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
     public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
 
+    private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
+
     static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
     protected DatabaseHelper mOpenHelper;
@@ -109,6 +111,8 @@
 
     private long mLastRestoreTimestamp = 0L;
 
+    private boolean mUseTestWorkspaceLayout;
+
     /**
      * $ adb shell dumpsys activity provider com.android.launcher3
      */
@@ -158,6 +162,7 @@
     private synchronized boolean prepForMigration(String dbFile, String targetTableName,
             Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
         if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
+            Log.e("b/198965093", "prepForMigration - target db is same as current: " + dbFile);
             return false;
         }
 
@@ -390,6 +395,14 @@
                 mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                 return null;
             }
+            case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
+                mUseTestWorkspaceLayout = true;
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
+                mUseTestWorkspaceLayout = false;
+                return null;
+            }
             case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
                 loadDefaultFavoritesIfNecessary();
                 return null;
@@ -427,7 +440,7 @@
                 Bundle result = new Bundle();
                 result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
                         prepForMigration(
-                                InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
+                                arg /* dbFile */,
                                 Favorites.TMP_TABLE,
                                 () -> mOpenHelper,
                                 () -> DatabaseHelper.createDatabaseHelper(
@@ -609,7 +622,8 @@
 
     private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
-        int defaultLayout = idp.defaultLayoutId;
+        int defaultLayout = mUseTestWorkspaceLayout
+                ? TEST_WORKSPACE_LAYOUT_RES_XML : idp.defaultLayoutId;
 
         if (getContext().getSystemService(UserManager.class).isDemoUser()
                 && idp.demoModeLayoutId != 0) {
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index e3aa758..a5c5c02 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,24 +1,19 @@
 package com.android.launcher3;
 
-import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.ViewDebug;
 import android.view.WindowInsets;
 
-import androidx.annotation.RequiresApi;
-
 import com.android.launcher3.graphics.SysUiScrim;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.window.WindowManagerProxy;
 
 import java.util.Collections;
 import java.util.List;
@@ -60,76 +55,12 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (Utilities.ATLEAST_R) {
-            insets = updateInsetsDueToTaskbar(insets);
-            Insets systemWindowInsets = insets.getInsetsIgnoringVisibility(
-                    WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
-            mTempRect.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
-                    systemWindowInsets.bottom);
-        } else {
-            mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
-                    insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
-        }
+        insets = WindowManagerProxy.INSTANCE.get(getContext())
+                .normalizeWindowInsets(getContext(), insets, mTempRect);
         handleSystemWindowInsets(mTempRect);
         return insets;
     }
 
-    /**
-     * Taskbar provides nav bar and tappable insets. However, taskbar is not attached immediately,
-     * and can be destroyed and recreated. Thus, instead of relying on taskbar being present to
-     * get its insets, we calculate them ourselves so they are stable regardless of whether taskbar
-     * is currently attached.
-     *
-     * @param oldInsets The system-provided insets, which we are modifying.
-     * @return The updated insets.
-     */
-    @RequiresApi(api = Build.VERSION_CODES.R)
-    private WindowInsets updateInsetsDueToTaskbar(WindowInsets oldInsets) {
-        if (!ApiWrapper.TASKBAR_DRAWN_IN_PROCESS) {
-            // 3P launchers based on Launcher3 should still be inset like normal.
-            return oldInsets;
-        }
-
-        WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
-
-        DeviceProfile dp = mActivity.getDeviceProfile();
-        Resources resources = getResources();
-
-        Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
-        Rect newNavInsets = new Rect(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
-                oldNavInsets.bottom);
-
-        if (dp.isLandscape) {
-            boolean isGesturalMode = ResourceUtils.getIntegerByName(
-                    "config_navBarInteractionMode",
-                    resources,
-                    INVALID_RESOURCE_HANDLE) == 2;
-            if (dp.isTablet || isGesturalMode) {
-                newNavInsets.bottom = dp.isTaskbarPresent
-                        ? 0
-                        : ResourceUtils.getNavbarSize("navigation_bar_height_landscape", resources);
-            } else {
-                int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
-                if (dp.isSeascape()) {
-                    newNavInsets.left = navWidth;
-                } else {
-                    newNavInsets.right = navWidth;
-                }
-            }
-        } else {
-            newNavInsets.bottom = dp.isTaskbarPresent
-                    ? 0
-                    : ResourceUtils.getNavbarSize("navigation_bar_height", resources);
-        }
-        updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
-        updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
-                Insets.of(newNavInsets));
-
-        mActivity.updateWindowInsets(updatedInsetsBuilder, oldInsets);
-
-        return updatedInsetsBuilder.build();
-    }
-
     @Override
     public void setInsets(Rect insets) {
         // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 048aaaa..66195f3 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -374,6 +374,12 @@
 
         public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
 
+        public static final String METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG =
+                "set_use_test_workspace_layout_flag";
+
+        public static final String METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG =
+                "clear_use_test_workspace_layout_flag";
+
         public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
 
         public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index be2cd88..baee49f 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -16,6 +16,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
@@ -91,6 +92,14 @@
                 }
             };
 
+    protected static final PageTranslationProvider DEFAULT_PAGE_TRANSLATION_PROVIDER =
+            new PageTranslationProvider(DEACCEL_2) {
+                @Override
+                public float getPageTranslation(int pageIndex) {
+                    return 0;
+                }
+            };
+
     private static final LauncherState[] sAllStates = new LauncherState[10];
 
     /**
@@ -288,6 +297,25 @@
         };
     }
 
+    /**
+     * Gets the translation provider for workspace pages.
+     */
+    public PageTranslationProvider getWorkspacePageTranslationProvider(Launcher launcher) {
+        if (this != SPRING_LOADED || !launcher.getDeviceProfile().isTwoPanels) {
+            return DEFAULT_PAGE_TRANSLATION_PROVIDER;
+        }
+        final float quarterPageSpacing = launcher.getWorkspace().getPageSpacing() / 4f;
+        return new PageTranslationProvider(DEACCEL_2) {
+            @Override
+            public float getPageTranslation(int pageIndex) {
+                boolean isRtl = launcher.getWorkspace().mIsRtl;
+                boolean isFirstPage = pageIndex % 2 == 0;
+                return ((isFirstPage && !isRtl) || (!isFirstPage && isRtl)) ? -quarterPageSpacing
+                        : quarterPageSpacing;
+            }
+        };
+    }
+
     @Override
     public LauncherState getHistoryForState(LauncherState previousState) {
         // No history is supported
@@ -318,6 +346,23 @@
         public abstract float getPageAlpha(int pageIndex);
     }
 
+    /**
+     * Provider for the translation and animation interpolation of workspace pages.
+     */
+    public abstract static class PageTranslationProvider {
+
+        public final Interpolator interpolator;
+
+        public PageTranslationProvider(Interpolator interpolator) {
+            this.interpolator = interpolator;
+        }
+
+        /**
+         * Gets the translation of the workspace page at the provided page index.
+         */
+        public abstract float getPageTranslation(int pageIndex);
+    }
+
     public static class ScaleAndTranslation {
         public float scale;
         public float translationX;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 2c14f07..1eb493b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -775,7 +775,7 @@
                     pageScrollChanged = true;
                     outPageScrolls[i] = pageScroll;
                 }
-                childStart += primaryDimension + getChildGap();
+                childStart += primaryDimension + getChildGap(i, i + delta);
 
                 // This makes sure that the space is added after the page, not after each panel
                 int lastPanel = mIsRtl ? 0 : panelCount - 1;
@@ -799,7 +799,7 @@
         return pageScrollChanged;
     }
 
-    protected int getChildGap() {
+    protected int getChildGap(int fromIndex, int toIndex) {
         return 0;
     }
 
diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/ResourceUtils.java
index ece123d..1c36db1 100644
--- a/src/com/android/launcher3/ResourceUtils.java
+++ b/src/com/android/launcher3/ResourceUtils.java
@@ -28,6 +28,9 @@
     public static final String NAVBAR_BOTTOM_GESTURE_LARGER_SIZE =
             "navigation_bar_gesture_larger_height";
 
+    public static final String NAVBAR_HEIGHT = "navigation_bar_height";
+    public static final String NAVBAR_HEIGHT_LANDSCAPE = "navigation_bar_height_landscape";
+
     public static int getNavbarSize(String resName, Resources res) {
         return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b3f5c03..a7fc2f5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -30,6 +30,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
+import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -323,37 +324,14 @@
             setPageSpacing(Math.max(maxInsets, maxPadding));
         }
 
-        updateWorkspaceScreensPadding();
+        updateCellLayoutPadding();
         updateWorkspaceWidgetsSizes();
     }
 
-    private void updateWorkspaceScreensPadding() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
-        int paddingBottom = grid.cellLayoutBottomPaddingPx;
-
-        int panelCount = getPanelCount();
-        int rightPanelModulus = mIsRtl ? 0 : panelCount - 1;
-        int leftPanelModulus = mIsRtl ? panelCount - 1 : 0;
-        int numberOfScreens = mScreenOrder.size();
-        for (int i = 0; i < numberOfScreens; i++) {
-            int paddingLeft = paddingLeftRight;
-            int paddingRight = paddingLeftRight;
-            // Add missing cellLayout border in-between panels.
-            if (panelCount > 1) {
-                if (i % panelCount == leftPanelModulus) {
-                    paddingRight += grid.cellLayoutBorderSpacePx.x / 2;
-                } else if (i % panelCount == rightPanelModulus) { // right side panel
-                    paddingLeft += grid.cellLayoutBorderSpacePx.x / 2;
-                } else { // middle panel
-                    paddingLeft += grid.cellLayoutBorderSpacePx.x / 2;
-                    paddingRight += grid.cellLayoutBorderSpacePx.x / 2;
-                }
-            }
-            // SparseArrayMap doesn't keep the order
-            mWorkspaceScreens.get(mScreenOrder.get(i))
-                    .setPadding(paddingLeft, 0, paddingRight, paddingBottom);
-        }
+    private void updateCellLayoutPadding() {
+        Rect padding = mLauncher.getDeviceProfile().cellLayoutPaddingPx;
+        mWorkspaceScreens.forEach(
+                s -> s.setPadding(padding.left, padding.top, padding.right, padding.bottom));
     }
 
     private void updateWorkspaceWidgetsSizes() {
@@ -651,7 +629,7 @@
                 mLauncher.getStateManager().getState(), newScreen, insertIndex);
 
         updatePageScrollValues();
-        updateWorkspaceScreensPadding();
+        updateCellLayoutPadding();
         return newScreen;
     }
 
@@ -1266,6 +1244,7 @@
         // different effects based on device performance. On at least one relatively high-end
         // device I've tried, translating the launcher causes things to get quite laggy.
         mLauncher.getDragLayer().setTranslationX(transX);
+        Log.d(BAD_STATE, "Workspace onOverlayScrollChanged DragLayer ALPHA_INDEX_OVERLAY=" + alpha);
         mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
     }
 
@@ -2484,21 +2463,27 @@
             }
         }
 
+        // Note, centerX represents the center of the object that is being dragged, visually. d.x
+        // represents the location of the finger within the dragged item.
+        float touchX;
+        float touchY = d.y;
+
+        // Go through the pages and check if the dragged item is inside one of them. This block
+        // is responsible for  determining whether we need to snap to a different screen.
         int nextPage = getNextPage();
-        IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage + 1);
-        if (isTwoPanelEnabled()) {
-            // If two panel is enabled, users can also drag items to nextPage + 2
-            pageIndexesToVerify.add(nextPage + 2);
-        }
-
-        int touchX = (int) Math.min(centerX, d.x);
-        int touchY = d.y;
-
-        // Go through the pages and check if the dragged item is inside one of them
+        IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage
+                + (isTwoPanelEnabled() ? 2 : 1));
         for (int pageIndex : pageIndexesToVerify) {
             if (layout != null || isPageInTransition()) {
                 break;
             }
+
+            // When deciding whether to perform a page switch, we need to consider the most extreme
+            // X coordinate between the finger location and the center of the object being dragged.
+            // This is either the max or the min of the two depending on whether dragging to the
+            // left / right, respectively.
+            touchX = ((((pageIndex < nextPage) && !mIsRtl) || pageIndex > nextPage && mIsRtl)
+                    ? Math.min(d.x, centerX) : Math.max(d.x, centerX));
             layout = verifyInsidePage(pageIndex, touchX, touchY);
         }
 
@@ -2507,12 +2492,16 @@
         // on one panel just choose the current page.
         if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
             if (isTwoPanelEnabled()) {
+                // When determining which panel to use within a single screen, we always use
+                // the centroid of the object rather than the finger.
+                touchX = centerX;
                 nextPage = getScreenCenter(getScrollX()) > touchX
                         ? (mIsRtl ? nextPage + 1 : nextPage) // left side
                         : (mIsRtl ? nextPage : nextPage + 1); // right side
             }
             layout = (CellLayout) getChildAt(nextPage);
         }
+
         if (layout != mDragTargetLayout) {
             setCurrentDropLayout(layout);
             setCurrentDragOverlappingLayout(layout);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index d94e84c..1e09b2d 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -39,6 +39,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_PAGE_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
@@ -49,6 +50,7 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.LauncherState.PageAlphaProvider;
+import com.android.launcher3.LauncherState.PageTranslationProvider;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
@@ -155,6 +157,12 @@
                 scaleAndTranslation.translationX, translationInterpolator);
         propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
                 scaleAndTranslation.translationY, translationInterpolator);
+        PageTranslationProvider pageTranslationProvider = state.getWorkspacePageTranslationProvider(
+                mLauncher);
+        for (int i = 0; i < childCount; i++) {
+            applyPageTranslation((CellLayout) mWorkspace.getChildAt(i), i, pageTranslationProvider,
+                    propertySetter, config);
+        }
 
         Interpolator hotseatTranslationInterpolator = config.getInterpolator(
                 ANIM_HOTSEAT_TRANSLATE, translationInterpolator);
@@ -202,6 +210,16 @@
                 pageAlpha, fadeInterpolator);
     }
 
+    private void applyPageTranslation(CellLayout cellLayout, int childIndex,
+            PageTranslationProvider pageTranslationProvider, PropertySetter propertySetter,
+            StateAnimationConfig config) {
+        float pageTranslation = pageTranslationProvider.getPageTranslation(childIndex);
+        Interpolator translationInterpolator = config.getInterpolator(
+                ANIM_WORKSPACE_PAGE_TRANSLATE_X, pageTranslationProvider.interpolator);
+        propertySetter.setFloat(cellLayout, VIEW_TRANSLATE_X, pageTranslation,
+                translationInterpolator);
+    }
+
     /**
      * Returns a spring based animator for the scale property of {@param workspace}.
      */
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 114f813..11e0a1f 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -26,12 +26,13 @@
 import androidx.core.graphics.ColorUtils;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.views.AppLauncher;
 
 import java.util.Objects;
 
@@ -40,8 +41,8 @@
  *
  * @param <T> Type of context inflating all apps.
  */
-public class ActivityAllAppsContainerView<T extends BaseDraggingActivity> extends
-        BaseAllAppsContainerView<T> {
+public class ActivityAllAppsContainerView<T extends Context & AppLauncher
+        & DeviceProfileListenable> extends BaseAllAppsContainerView<T> {
 
     protected SearchUiManager mSearchUiManager;
     /**
@@ -103,13 +104,8 @@
         }
     }
 
-    /** Handles selection on focused view and returns {@code true} on success. */
-    public boolean launchHighlightedItem() {
-        return getMainAdapterProvider().launchHighlightedItem();
-    }
-
     @Override
-    protected SearchAdapterProvider<?> createMainAdapterProvider() {
+    protected final SearchAdapterProvider<?> createMainAdapterProvider() {
         return mActivityContext.createSearchAdapterProvider(this);
     }
 
@@ -256,4 +252,11 @@
         layoutParams.removeRule(RelativeLayout.BELOW);
         layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
     }
+
+    @Override
+    protected BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<T> mAppsList,
+            BaseAdapterProvider[] adapterProviders) {
+        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
+                adapterProviders);
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index f1ca9c0..58df50c 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -15,36 +15,20 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
-
 import android.content.Context;
-import android.content.res.Resources;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.TextView;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.core.view.accessibility.AccessibilityEventCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityRecordCompat;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.views.ActivityContext;
 
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -53,111 +37,26 @@
  * @param <T> Type of context inflating all apps.
  */
 public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
-        RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
+        BaseAllAppsAdapter<T> {
 
     public static final String TAG = "AppsGridAdapter";
+    private final GridLayoutManager mGridLayoutMgr;
+    private final GridSpanSizer mGridSizer;
 
-    // A normal icon
-    public static final int VIEW_TYPE_ICON = 1 << 1;
-    // The message shown when there are no filtered results
-    public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
-    // The message to continue to a market search when there are no filtered results
-    public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
-
-    // We use various dividers for various purposes.  They share enough attributes to reuse layouts,
-    // but differ in enough attributes to require different view types
-
-    // A divider that separates the apps list and the search market button
-    public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
-
-    // Common view type masks
-    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
-    public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
-
-
-    private final BaseAdapterProvider[] mAdapterProviders;
-
-    /**
-     * ViewHolder for each icon.
-     */
-    public static class ViewHolder extends RecyclerView.ViewHolder {
-
-        public ViewHolder(View v) {
-            super(v);
-        }
+    public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
+            AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
+        super(activityContext, inflater, apps, adapterProviders);
+        mGridSizer = new GridSpanSizer();
+        mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
+        mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
+        setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
     }
 
     /**
-     * Info about a particular adapter item (can be either section or app)
+     * Returns the grid layout manager.
      */
-    public static class AdapterItem {
-        /** Common properties */
-        // The index of this adapter item in the list
-        public int position;
-        // The type of this item
-        public int viewType;
-
-        // The section name of this item.  Note that there can be multiple items with different
-        // sectionNames in the same section
-        public String sectionName = null;
-        // The row that this item shows up on
-        public int rowIndex;
-        // The index of this app in the row
-        public int rowAppIndex;
-        // The associated ItemInfoWithIcon for the item
-        public ItemInfoWithIcon itemInfo = null;
-        // The index of this app not including sections
-        public int appIndex = -1;
-        // Search section associated to result
-        public DecorationInfo decorationInfo = null;
-
-        /**
-         * Factory method for AppIcon AdapterItem
-         */
-        public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
-                int appIndex) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = VIEW_TYPE_ICON;
-            item.position = pos;
-            item.sectionName = sectionName;
-            item.itemInfo = appInfo;
-            item.appIndex = appIndex;
-            return item;
-        }
-
-        /**
-         * Factory method for empty search results view
-         */
-        public static AdapterItem asEmptySearch(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = VIEW_TYPE_EMPTY_SEARCH;
-            item.position = pos;
-            return item;
-        }
-
-        /**
-         * Factory method for a dividerView in AllAppsSearch
-         */
-        public static AdapterItem asAllAppsDivider(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
-            item.position = pos;
-            return item;
-        }
-
-        /**
-         * Factory method for a market search button
-         */
-        public static AdapterItem asMarketSearch(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = VIEW_TYPE_SEARCH_MARKET;
-            item.position = pos;
-            return item;
-        }
-
-        protected boolean isCountedForAccessibility() {
-            return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
-        }
+    public RecyclerView.LayoutManager getLayoutManager() {
+        return mGridLayoutMgr;
     }
 
     /**
@@ -217,7 +116,7 @@
          */
         private int getRowsNotForAccessibility(int adapterPosition) {
             List<AdapterItem> items = mApps.getAdapterItems();
-            adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1);
+            adapterPosition = Math.max(adapterPosition, items.size() - 1);
             int extraRows = 0;
             for (int i = 0; i <= adapterPosition; i++) {
                 if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_ICON)) {
@@ -228,6 +127,20 @@
         }
     }
 
+    @Override
+    public void setAppsPerRow(int appsPerRow) {
+        mAppsPerRow = appsPerRow;
+        int totalSpans = mAppsPerRow;
+        for (BaseAdapterProvider adapterProvider : mAdapterProviders) {
+            for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) {
+                if (totalSpans % itemPerRow != 0) {
+                    totalSpans *= itemPerRow;
+                }
+            }
+        }
+        mGridLayoutMgr.setSpanCount(totalSpans);
+    }
+
     /**
      * Helper class to size the grid items.
      */
@@ -255,202 +168,4 @@
             }
         }
     }
-
-    private final T mActivityContext;
-    private final LayoutInflater mLayoutInflater;
-    private final AlphabeticalAppsList<T> mApps;
-    private final GridLayoutManager mGridLayoutMgr;
-    private final GridSpanSizer mGridSizer;
-
-    private final OnClickListener mOnIconClickListener;
-    private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
-
-    private int mAppsPerRow;
-
-    private OnFocusChangeListener mIconFocusListener;
-
-    // The text to show when there are no search results and no market search handler.
-    protected String mEmptySearchMessage;
-    // The click listener to send off to the market app, updated each time the search query changes.
-    private OnClickListener mMarketSearchClickListener;
-
-    private final int mExtraHeight;
-
-    public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
-            AlphabeticalAppsList<T> apps, BaseAdapterProvider[] adapterProviders) {
-        Resources res = activityContext.getResources();
-        mActivityContext = activityContext;
-        mApps = apps;
-        mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
-        mGridSizer = new GridSpanSizer();
-        mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
-        mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
-        mLayoutInflater = inflater;
-
-        mOnIconClickListener = mActivityContext.getItemOnClickListener();
-
-        mAdapterProviders = adapterProviders;
-        setAppsPerRow(mActivityContext.getDeviceProfile().numShownAllAppsColumns);
-        mExtraHeight = mActivityContext.getResources().getDimensionPixelSize(
-                R.dimen.all_apps_height_extra);
-    }
-
-    public void setAppsPerRow(int appsPerRow) {
-        mAppsPerRow = appsPerRow;
-        int totalSpans = mAppsPerRow;
-        for (BaseAdapterProvider adapterProvider : mAdapterProviders) {
-            for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) {
-                if (totalSpans % itemPerRow != 0) {
-                    totalSpans *= itemPerRow;
-                }
-            }
-        }
-        mGridLayoutMgr.setSpanCount(totalSpans);
-    }
-
-    /**
-     * Sets the long click listener for icons
-     */
-    public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
-        mOnIconLongClickListener = listener;
-    }
-
-    public static boolean isDividerViewType(int viewType) {
-        return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
-    }
-
-    public static boolean isIconViewType(int viewType) {
-        return isViewType(viewType, VIEW_TYPE_MASK_ICON);
-    }
-
-    public static boolean isViewType(int viewType, int viewTypeMask) {
-        return (viewType & viewTypeMask) != 0;
-    }
-
-    public void setIconFocusListener(OnFocusChangeListener focusListener) {
-        mIconFocusListener = focusListener;
-    }
-
-    /**
-     * Sets the last search query that was made, used to show when there are no results and to also
-     * seed the intent for searching the market.
-     */
-    public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) {
-        Resources res = mActivityContext.getResources();
-        mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
-        mMarketSearchClickListener = marketSearchClickListener;
-    }
-
-    /**
-     * Returns the grid layout manager.
-     */
-    public GridLayoutManager getLayoutManager() {
-        return mGridLayoutMgr;
-    }
-
-    @Override
-    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        switch (viewType) {
-            case VIEW_TYPE_ICON:
-                int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
-                        : R.layout.all_apps_icon_twoline;
-                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
-                        layout, parent, false);
-                icon.setLongPressTimeoutFactor(1f);
-                icon.setOnFocusChangeListener(mIconFocusListener);
-                icon.setOnClickListener(mOnIconClickListener);
-                icon.setOnLongClickListener(mOnIconLongClickListener);
-                // Ensure the all apps icon height matches the workspace icons in portrait mode.
-                icon.getLayoutParams().height =
-                        mActivityContext.getDeviceProfile().allAppsCellHeightPx;
-                if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
-                    icon.getLayoutParams().height += mExtraHeight;
-                }
-                return new ViewHolder(icon);
-            case VIEW_TYPE_EMPTY_SEARCH:
-                return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
-                        parent, false));
-            case VIEW_TYPE_SEARCH_MARKET:
-                View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
-                        parent, false);
-                searchMarketView.setOnClickListener(mMarketSearchClickListener);
-                return new ViewHolder(searchMarketView);
-            case VIEW_TYPE_ALL_APPS_DIVIDER:
-                return new ViewHolder(mLayoutInflater.inflate(
-                        R.layout.all_apps_divider, parent, false));
-            default:
-                BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
-                if (adapterProvider != null) {
-                    return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
-                }
-                throw new RuntimeException("Unexpected view type" + viewType);
-        }
-    }
-
-    @Override
-    public void onBindViewHolder(ViewHolder holder, int position) {
-        switch (holder.getItemViewType()) {
-            case VIEW_TYPE_ICON:
-                AdapterItem adapterItem = mApps.getAdapterItems().get(position);
-                BubbleTextView icon = (BubbleTextView) holder.itemView;
-                icon.reset();
-                if (adapterItem.itemInfo instanceof AppInfo) {
-                    icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
-                } else {
-                    icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
-                }
-                break;
-            case VIEW_TYPE_EMPTY_SEARCH:
-                TextView emptyViewText = (TextView) holder.itemView;
-                emptyViewText.setText(mEmptySearchMessage);
-                emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
-                        Gravity.START | Gravity.CENTER_VERTICAL);
-                break;
-            case VIEW_TYPE_SEARCH_MARKET:
-                TextView searchView = (TextView) holder.itemView;
-                if (mMarketSearchClickListener != null) {
-                    searchView.setVisibility(View.VISIBLE);
-                } else {
-                    searchView.setVisibility(View.GONE);
-                }
-                break;
-            case VIEW_TYPE_ALL_APPS_DIVIDER:
-                // nothing to do
-                break;
-            default:
-                BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType());
-                if (adapterProvider != null) {
-                    adapterProvider.onBindView(holder, position);
-                }
-        }
-    }
-
-    @Override
-    public void onViewRecycled(@NonNull ViewHolder holder) {
-        super.onViewRecycled(holder);
-    }
-
-    @Override
-    public boolean onFailedToRecycleView(ViewHolder holder) {
-        // Always recycle and we will reset the view when it is bound
-        return true;
-    }
-
-    @Override
-    public int getItemCount() {
-        return mApps.getAdapterItems().size();
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        AdapterItem item = mApps.getAdapterItems().get(position);
-        return item.viewType;
-    }
-
-    @Nullable
-    private BaseAdapterProvider getAdapterProvider(int viewType) {
-        return Arrays.stream(mAdapterProviders).filter(
-                adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse(
-                null);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index c2cb845..7dbe711 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,6 +19,7 @@
 import static android.view.View.MeasureSpec.UNSPECIFIED;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
 import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
@@ -203,6 +204,7 @@
         StatsLogManager mgr = ActivityContext.lookupContext(getContext()).getStatsLogManager();
         switch (state) {
             case SCROLL_STATE_DRAGGING:
+                mgr.logger().log(LAUNCHER_ALLAPPS_SCROLLED);
                 requestFocus();
                 mgr.logger().sendToInteractionJankMonitor(
                         LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 1a1d521..16264da 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -18,7 +18,7 @@
 
 import android.content.Context;
 
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -81,8 +81,8 @@
     private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
 
     // The of ordered component names as a result of a search query
-    private ArrayList<AdapterItem> mSearchResults;
-    private AllAppsGridAdapter<T> mAdapter;
+    private final ArrayList<AdapterItem> mSearchResults = new ArrayList<>();
+    private BaseAllAppsAdapter<T> mAdapter;
     private AppInfoComparator mAppNameComparator;
     private final int mNumAppsPerRow;
     private int mNumAppRowsInAdapter;
@@ -106,7 +106,7 @@
     /**
      * Sets the adapter to notify when this dataset changes.
      */
-    public void setAdapter(AllAppsGridAdapter<T> adapter) {
+    public void setAdapter(BaseAllAppsAdapter<T> adapter) {
         mAdapter = adapter;
     }
 
@@ -171,30 +171,33 @@
      * Returns whether there are is a filter set.
      */
     public boolean hasFilter() {
-        return (mSearchResults != null);
+        return !mSearchResults.isEmpty();
     }
 
     /**
      * Returns whether there are no filtered results.
      */
     public boolean hasNoFilteredResults() {
-        return (mSearchResults != null) && mAccessibilityResultsCount == 0;
+        return hasFilter() && mAccessibilityResultsCount == 0;
     }
 
     /**
      * Sets results list for search
      */
     public boolean setSearchResults(ArrayList<AdapterItem> results) {
-        if (!Objects.equals(results, mSearchResults)) {
-            mSearchResults = results;
-            updateAdapterItems();
-            return true;
+        if (Objects.equals(results, mSearchResults)) {
+            return false;
         }
-        return false;
+        mSearchResults.clear();
+        if (results != null) {
+            mSearchResults.addAll(results);
+        }
+        updateAdapterItems();
+        return true;
     }
 
     public boolean appendSearchResults(ArrayList<AdapterItem> results) {
-        if (mSearchResults != null && results != null && results.size() > 0) {
+        if (hasFilter() && results != null && results.size() > 0) {
             updateSearchAdapterItems(results, mSearchResults.size());
             refreshRecyclerView();
             return true;
@@ -259,7 +262,7 @@
         }
 
         // Recompose the set of adapter items from the current set of apps
-        if (mSearchResults == null) {
+        if (mSearchResults.isEmpty()) {
             updateAdapterItems();
         }
     }
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
new file mode 100644
index 0000000..1d1960d
--- /dev/null
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -0,0 +1,333 @@
+/*
+ * 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.allapps;
+
+import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.views.ActivityContext;
+
+import java.util.Arrays;
+
+/**
+ * Adapter for all the apps.
+ *
+ * @param <T> Type of context inflating all apps.
+ */
+public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> extends
+        RecyclerView.Adapter<BaseAllAppsAdapter.ViewHolder> {
+
+    public static final String TAG = "BaseAllAppsAdapter";
+
+    // A normal icon
+    public static final int VIEW_TYPE_ICON = 1 << 1;
+    // The message shown when there are no filtered results
+    public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
+    // The message to continue to a market search when there are no filtered results
+    public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
+
+    // We use various dividers for various purposes.  They share enough attributes to reuse layouts,
+    // but differ in enough attributes to require different view types
+
+    // A divider that separates the apps list and the search market button
+    public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
+
+    // Common view type masks
+    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
+    public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
+
+
+    protected final BaseAdapterProvider[] mAdapterProviders;
+
+    /**
+     * ViewHolder for each icon.
+     */
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+
+        public ViewHolder(View v) {
+            super(v);
+        }
+    }
+
+    /** Sets the number of apps to be displayed in one row of the all apps screen. */
+    public abstract void setAppsPerRow(int appsPerRow);
+
+    /**
+     * Info about a particular adapter item (can be either section or app)
+     */
+    public static class AdapterItem {
+        /** Common properties */
+        // The index of this adapter item in the list
+        public int position;
+        // The type of this item
+        public int viewType;
+
+        // The section name of this item.  Note that there can be multiple items with different
+        // sectionNames in the same section
+        public String sectionName = null;
+        // The row that this item shows up on
+        public int rowIndex;
+        // The index of this app in the row
+        public int rowAppIndex;
+        // The associated ItemInfoWithIcon for the item
+        public ItemInfoWithIcon itemInfo = null;
+        // The index of this app not including sections
+        public int appIndex = -1;
+        // Search section associated to result
+        public DecorationInfo decorationInfo = null;
+
+        /**
+         * Factory method for AppIcon AdapterItem
+         */
+        public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
+                int appIndex) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_ICON;
+            item.position = pos;
+            item.sectionName = sectionName;
+            item.itemInfo = appInfo;
+            item.appIndex = appIndex;
+            return item;
+        }
+
+        /**
+         * Factory method for empty search results view
+         */
+        public static AdapterItem asEmptySearch(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_EMPTY_SEARCH;
+            item.position = pos;
+            return item;
+        }
+
+        /**
+         * Factory method for a dividerView in AllAppsSearch
+         */
+        public static AdapterItem asAllAppsDivider(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
+            item.position = pos;
+            return item;
+        }
+
+        /**
+         * Factory method for a market search button
+         */
+        public static AdapterItem asMarketSearch(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_SEARCH_MARKET;
+            item.position = pos;
+            return item;
+        }
+
+        protected boolean isCountedForAccessibility() {
+            return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
+        }
+    }
+
+    protected final T mActivityContext;
+    protected final AlphabeticalAppsList<T> mApps;
+    // The text to show when there are no search results and no market search handler.
+    protected String mEmptySearchMessage;
+    protected int mAppsPerRow;
+
+    private final LayoutInflater mLayoutInflater;
+    private final OnClickListener mOnIconClickListener;
+    private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
+    private OnFocusChangeListener mIconFocusListener;
+    // The click listener to send off to the market app, updated each time the search query changes.
+    private OnClickListener mMarketSearchClickListener;
+    private final int mExtraHeight;
+
+    public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
+            AlphabeticalAppsList<T> apps, BaseAdapterProvider[] adapterProviders) {
+        Resources res = activityContext.getResources();
+        mActivityContext = activityContext;
+        mApps = apps;
+        mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
+        mLayoutInflater = inflater;
+
+        mOnIconClickListener = mActivityContext.getItemOnClickListener();
+
+        mAdapterProviders = adapterProviders;
+        mExtraHeight = res.getDimensionPixelSize(R.dimen.all_apps_height_extra);
+    }
+
+    /**
+     * Sets the long click listener for icons
+     */
+    public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
+        mOnIconLongClickListener = listener;
+    }
+
+    /** Checks if the passed viewType represents all apps divider. */
+    public static boolean isDividerViewType(int viewType) {
+        return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
+    }
+
+    /** Checks if the passed viewType represents all apps icon. */
+    public static boolean isIconViewType(int viewType) {
+        return isViewType(viewType, VIEW_TYPE_MASK_ICON);
+    }
+
+    public void setIconFocusListener(OnFocusChangeListener focusListener) {
+        mIconFocusListener = focusListener;
+    }
+
+    /**
+     * Sets the last search query that was made, used to show when there are no results and to also
+     * seed the intent for searching the market.
+     */
+    public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) {
+        Resources res = mActivityContext.getResources();
+        mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
+        mMarketSearchClickListener = marketSearchClickListener;
+    }
+
+    /**
+     * Returns the layout manager.
+     */
+    public abstract RecyclerView.LayoutManager getLayoutManager();
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        switch (viewType) {
+            case VIEW_TYPE_ICON:
+                int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
+                        : R.layout.all_apps_icon_twoline;
+                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
+                        layout, parent, false);
+                icon.setLongPressTimeoutFactor(1f);
+                icon.setOnFocusChangeListener(mIconFocusListener);
+                icon.setOnClickListener(mOnIconClickListener);
+                icon.setOnLongClickListener(mOnIconLongClickListener);
+                // Ensure the all apps icon height matches the workspace icons in portrait mode.
+                icon.getLayoutParams().height =
+                        mActivityContext.getDeviceProfile().allAppsCellHeightPx;
+                if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
+                    icon.getLayoutParams().height += mExtraHeight;
+                }
+                return new ViewHolder(icon);
+            case VIEW_TYPE_EMPTY_SEARCH:
+                return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
+                        parent, false));
+            case VIEW_TYPE_SEARCH_MARKET:
+                View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
+                        parent, false);
+                searchMarketView.setOnClickListener(mMarketSearchClickListener);
+                return new ViewHolder(searchMarketView);
+            case VIEW_TYPE_ALL_APPS_DIVIDER:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.all_apps_divider, parent, false));
+            default:
+                BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
+                if (adapterProvider != null) {
+                    return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
+                }
+                throw new RuntimeException("Unexpected view type" + viewType);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        switch (holder.getItemViewType()) {
+            case VIEW_TYPE_ICON:
+                AdapterItem adapterItem = mApps.getAdapterItems().get(position);
+                BubbleTextView icon = (BubbleTextView) holder.itemView;
+                icon.reset();
+                if (adapterItem.itemInfo instanceof AppInfo) {
+                    icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
+                } else {
+                    icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
+                }
+                break;
+            case VIEW_TYPE_EMPTY_SEARCH:
+                TextView emptyViewText = (TextView) holder.itemView;
+                emptyViewText.setText(mEmptySearchMessage);
+                emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
+                        Gravity.START | Gravity.CENTER_VERTICAL);
+                break;
+            case VIEW_TYPE_SEARCH_MARKET:
+                TextView searchView = (TextView) holder.itemView;
+                if (mMarketSearchClickListener != null) {
+                    searchView.setVisibility(View.VISIBLE);
+                } else {
+                    searchView.setVisibility(View.GONE);
+                }
+                break;
+            case VIEW_TYPE_ALL_APPS_DIVIDER:
+                // nothing to do
+                break;
+            default:
+                BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType());
+                if (adapterProvider != null) {
+                    adapterProvider.onBindView(holder, position);
+                }
+        }
+    }
+
+    @Override
+    public void onViewRecycled(@NonNull ViewHolder holder) {
+        super.onViewRecycled(holder);
+    }
+
+    @Override
+    public boolean onFailedToRecycleView(ViewHolder holder) {
+        // Always recycle and we will reset the view when it is bound
+        return true;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mApps.getAdapterItems().size();
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        AdapterItem item = mApps.getAdapterItems().get(position);
+        return item.viewType;
+    }
+
+    protected static boolean isViewType(int viewType, int viewTypeMask) {
+        return (viewType & viewTypeMask) != 0;
+    }
+
+    @Nullable
+    protected BaseAdapterProvider getAdapterProvider(int viewType) {
+        return Arrays.stream(mAdapterProviders).filter(
+                adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse(
+                null);
+    }
+}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index bfc7515..f913aa9 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -44,7 +44,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.ColorUtils;
-import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.DeviceProfile;
@@ -443,8 +442,8 @@
         if (showTabs == mUsingTabs && !force) {
             return;
         }
-        replaceRVContainer(showTabs);
         mUsingTabs = showTabs;
+        replaceRVContainer(mUsingTabs);
 
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
@@ -697,18 +696,21 @@
         return ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, blendRatio);
     }
 
+    protected abstract BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<T> mAppsList,
+            BaseAdapterProvider[] adapterProviders);
+
     protected int getHeaderBottom() {
         return (int) getTranslationY();
     }
 
-    /** Holds a {@link AllAppsGridAdapter} and related fields. */
+    /** Holds a {@link BaseAllAppsAdapter} and related fields. */
     public class AdapterHolder {
         public static final int MAIN = 0;
         public static final int WORK = 1;
 
         private final boolean mIsWork;
-        public final AllAppsGridAdapter<T> adapter;
-        final LinearLayoutManager mLayoutManager;
+        public final BaseAllAppsAdapter<T> adapter;
+        final RecyclerView.LayoutManager mLayoutManager;
         final AlphabeticalAppsList<T> mAppsList;
         final Rect mPadding = new Rect();
         AllAppsRecyclerView mRecyclerView;
@@ -724,8 +726,7 @@
                             mWorkManager.getAdapterProvider()}
                             : new BaseAdapterProvider[]{mMainAdapterProvider};
 
-            adapter = new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
-                    adapterProviders);
+            adapter = getAdapter(mAppsList, adapterProviders);
             mAppsList.setAdapter(adapter);
             mLayoutManager = adapter.getLayoutManager();
         }
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index f9055ee..6299657 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -65,4 +65,7 @@
      * sets highlight result's title
      */
     default void setFocusedResultTitle(@Nullable  CharSequence title) { }
+
+    /** Refresh the currently displayed list of results. */
+    default void refreshResults() {}
 }
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 0137e2a..a6a47a7 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -29,14 +29,13 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * An interface to a search box that AllApps can command.
@@ -45,7 +44,7 @@
         implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
         OnFocusChangeListener {
 
-    protected BaseDraggingActivity mLauncher;
+    protected ActivityContext mLauncher;
     protected SearchCallback<AdapterItem> mCallback;
     protected ExtendedEditText mInput;
     protected String mQuery;
@@ -62,7 +61,7 @@
      */
     public final void initialize(
             SearchAlgorithm<AdapterItem> searchAlgorithm, ExtendedEditText input,
-            BaseDraggingActivity launcher, SearchCallback<AdapterItem> callback) {
+            ActivityContext launcher, SearchCallback<AdapterItem> callback) {
         mCallback = callback;
         mLauncher = launcher;
 
@@ -125,7 +124,7 @@
             mLauncher.getStatsLogManager().logger()
                     .log(LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME);
             // selectFocusedView should return SearchTargetEvent that is passed onto onClick
-            return Launcher.getLauncher(mLauncher).getAppsView().launchHighlightedItem();
+            return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem();
         }
         return false;
     }
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 4a886a4..893e547 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -32,17 +32,17 @@
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
 import com.android.launcher3.allapps.SearchUiManager;
 import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 
@@ -53,7 +53,7 @@
         implements SearchUiManager, SearchCallback<AdapterItem>,
         AllAppsStore.OnUpdateListener, Insettable {
 
-    private final BaseDraggingActivity mLauncher;
+    private final ActivityContext mLauncher;
     private final AllAppsSearchBarController mSearchBarController;
     private final SpannableStringBuilder mSearchQueryBuilder;
 
@@ -74,7 +74,7 @@
     public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        mLauncher = BaseDraggingActivity.fromContext(context);
+        mLauncher = ActivityContext.lookupContext(context);
         mSearchBarController = new AllAppsSearchBarController();
 
         mSearchQueryBuilder = new SpannableStringBuilder();
@@ -134,7 +134,7 @@
         mApps = appsView.getApps();
         mAppsView = appsView;
         mSearchBarController.initialize(
-                new DefaultAppSearchAlgorithm(mLauncher),
+                new DefaultAppSearchAlgorithm(getContext()),
                 this, mLauncher, this);
     }
 
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 1f854c6..222c8fe 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -23,7 +23,7 @@
 import androidx.annotation.AnyThread;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index 2fe4915..a95bd51 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -23,20 +23,20 @@
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.views.AppLauncher;
 
 /**
- * Provides views for local search results
+ * Provides views for local search results.
  */
-public class DefaultSearchAdapterProvider extends SearchAdapterProvider<BaseDraggingActivity> {
+public class DefaultSearchAdapterProvider extends SearchAdapterProvider<AppLauncher> {
 
     private final RecyclerView.ItemDecoration mDecoration;
     private View mHighlightedView;
 
-    public DefaultSearchAdapterProvider(BaseDraggingActivity launcher) {
+    public DefaultSearchAdapterProvider(AppLauncher launcher) {
         super(launcher);
         mDecoration = new RecyclerView.ItemDecoration() {
             @Override
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 85ca280..1cc0c21 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -19,7 +19,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index bd52158..40fa0cf 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -25,7 +25,7 @@
 import androidx.annotation.FloatRange;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.window.RefreshRateTracker;
 
 /**
  * Utility class to build an object animator which follows the same path as a spring animation for
@@ -134,7 +134,7 @@
     }
 
     public SpringAnimationBuilder computeParams() {
-        int singleFrameMs = DisplayController.getSingleFrameMs(mContext);
+        int singleFrameMs = RefreshRateTracker.getSingleFrameMs(mContext);
         double naturalFreq = Math.sqrt(mStiffness);
         double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8b2184d..7727fae 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -52,7 +52,7 @@
      * Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
      * and should be modified at a project level.
      */
-    public static final boolean QSB_ON_FIRST_SCREEN = true;
+    public static final boolean QSB_ON_FIRST_SCREEN = BuildConfig.QSB_ON_FIRST_SCREEN;
 
     /**
      * Feature flag to handle define config changes dynamically instead of killing the process.
@@ -89,13 +89,13 @@
     public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
             "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
 
-    public static final BooleanFlag ENABLE_ONE_SEARCH = new DeviceFlag("ENABLE_ONE_SEARCH", false,
-            "Use homescreen search box to complete allApps searches");
-
     public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
             getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false,
                     "Keep All Apps search bar at the bottom (but above keyboard if open)");
 
+    public static final BooleanFlag ENABLE_QUICK_SEARCH = new DeviceFlag("ENABLE_QUICK_SEARCH",
+            true, "Use quick search behavior.");
+
     public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
             "COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
 
@@ -246,6 +246,10 @@
             "ENABLE_ALL_APPS_IN_TASKBAR", true,
             "Enables accessing All Apps from the system Taskbar.");
 
+    public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag(
+            "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
+            "Enables One Search box in Taskbar All Apps.");
+
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(
             "ENABLE_SPLIT_FROM_WORKSPACE", true,
             "Enable initiating split screen from workspace.");
@@ -254,6 +258,9 @@
             "ENABLE_NEW_MIGRATION_LOGIC", true,
             "Enable the new grid migration logic, keeping pages when src < dest");
 
+    public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag(
+            "ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index b914ae2..a3945fd 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -98,6 +98,7 @@
     final ValueAnimator mAnim;
     // Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends.
     private boolean mAnimStarted;
+    private Runnable mOnAnimEndCallback = null;
 
     private int mLastTouchX;
     private int mLastTouchY;
@@ -180,6 +181,14 @@
             public void onAnimationStart(Animator animation) {
                 mAnimStarted = true;
             }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (mOnAnimEndCallback != null) {
+                    mOnAnimEndCallback.run();
+                }
+            }
         });
 
         setDragRegion(new Rect(0, 0, width, height));
@@ -199,6 +208,10 @@
         setWillNotDraw(false);
     }
 
+    public void setOnAnimationEndCallback(Runnable callback) {
+        mOnAnimEndCallback = callback;
+    }
+
     /**
      * Initialize {@code #mIconDrawable} if the item can be represented using
      * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 1e6342c..8916519 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -24,7 +24,7 @@
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 65991e4..3d5aef5 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -252,7 +252,7 @@
     }
 
     @Override
-    protected int getChildGap() {
+    protected int getChildGap(int fromIndex, int toIndex) {
         return getPaddingLeft() + getPaddingRight();
     }
 
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2459e09..3dfece7 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -87,6 +87,7 @@
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
@@ -128,7 +129,8 @@
         public PreviewContext(Context base, InvariantDeviceProfile idp) {
             super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
                     LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
-                    CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE);
+                    CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE,
+                    WindowManagerProxy.INSTANCE);
             mIdp = idp;
             mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
             mObjectMap.put(LauncherAppState.INSTANCE,
@@ -223,21 +225,21 @@
         mHotseat.resetLayout(false);
 
         CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
-        firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
-                mDp.workspacePadding.top,
+        firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingPx.left,
+                mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
                 (mDp.isTwoPanels ? mDp.cellLayoutBorderSpacePx.x / 2
-                        : mDp.workspacePadding.right) + mDp.cellLayoutPaddingLeftRightPx,
-                mDp.workspacePadding.bottom
+                        : mDp.workspacePadding.right) + mDp.cellLayoutPaddingPx.right,
+                mDp.workspacePadding.bottom + mDp.cellLayoutPaddingPx.bottom
         );
         mWorkspaceScreens.put(FIRST_SCREEN_ID, firstScreen);
 
         if (mDp.isTwoPanels) {
             CellLayout rightPanel = mRootView.findViewById(R.id.workspace_right);
             rightPanel.setPadding(
-                    mDp.cellLayoutBorderSpacePx.x / 2 + mDp.cellLayoutPaddingLeftRightPx,
-                    mDp.workspacePadding.top,
-                    mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
-                    mDp.workspacePadding.bottom
+                    mDp.cellLayoutBorderSpacePx.x / 2  + mDp.cellLayoutPaddingPx.left,
+                    mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
+                    mDp.workspacePadding.right + mDp.cellLayoutPaddingPx.right,
+                    mDp.workspacePadding.bottom + mDp.cellLayoutPaddingPx.bottom
             );
             mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
         }
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 308a8f2..7ba2317 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.icons;
 
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
@@ -340,14 +341,15 @@
         Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
                 iconRequestInfos.stream()
                         .filter(iconRequest -> {
-                            if (iconRequest.itemInfo.getTargetComponent() != null) {
-                                return true;
+                            if (iconRequest.itemInfo.getTargetComponent() == null) {
+                                Log.i(TAG,
+                                        "Skipping Item info with null component name: "
+                                                + iconRequest.itemInfo);
+                                iconRequest.itemInfo.bitmap = getDefaultIcon(
+                                        iconRequest.itemInfo.user);
+                                return false;
                             }
-                            Log.i(TAG,
-                                    "Skipping Item info with null component name: "
-                                            + iconRequest.itemInfo);
-                            iconRequest.itemInfo.bitmap = getDefaultIcon(iconRequest.itemInfo.user);
-                            return false;
+                            return true;
                         })
                         .collect(groupingBy(iconRequest ->
                                 Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));
@@ -356,6 +358,17 @@
         iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> {
             Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap =
                     filteredList.stream()
+                            .filter(iconRequest -> {
+                                // Filter out icons that should not share the same bitmap and title
+                                if (iconRequest.itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+                                    Log.e(TAG,
+                                            "Skipping Item info for deep shortcut: "
+                                                    + iconRequest.itemInfo,
+                                            new IllegalStateException());
+                                    return false;
+                                }
+                                return true;
+                            })
                             .collect(groupingBy(iconRequest ->
                                     iconRequest.itemInfo.getTargetComponent()));
 
@@ -440,7 +453,7 @@
                             cn,
                             sectionKey.first);
                 }
-                if (loadFallbackTitle && TextUtils.isEmpty(entry.title)) {
+                if (loadFallbackTitle && TextUtils.isEmpty(entry.title) && lai != null) {
                     loadFallbackTitle(
                             lai,
                             entry,
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 0d2bc37..e6dc21e 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -527,7 +527,32 @@
         LAUNCHER_TASKBAR_LONGPRESS_SHOW(897),
 
         @UiEvent(doc = "User clicks on the search icon on header to launch search in app.")
-        LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913);
+        LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913),
+
+        @UiEvent(doc = "User is shown the back gesture navigation tutorial step.")
+        LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_SHOWN(959),
+
+        @UiEvent(doc = "User is shown the home gesture navigation tutorial step.")
+        LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_SHOWN(960),
+
+        @UiEvent(doc = "User is shown the overview gesture navigation tutorial step.")
+        LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_SHOWN(961),
+
+        @UiEvent(doc = "User completed the back gesture navigation tutorial step.")
+        LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_COMPLETED(962),
+
+        @UiEvent(doc = "User completed the home gesture navigation tutorial step.")
+        LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_COMPLETED(963),
+
+        @UiEvent(doc = "User completed the overview gesture navigation tutorial step.")
+        LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_COMPLETED(964),
+
+        @UiEvent(doc = "User skips the gesture navigation tutorial.")
+        LAUNCHER_GESTURE_TUTORIAL_SKIPPED(965),
+
+        @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+                + "result page etc.")
+        LAUNCHER_ALLAPPS_SCROLLED(985);
 
         // ADD MORE
 
@@ -562,7 +587,7 @@
     }
 
     /**
-     * Helps to construct and write the log message.
+     * Helps to construct and log launcher event.
      */
     public interface StatsLogger {
 
@@ -662,6 +687,64 @@
     }
 
     /**
+     * Helps to construct and log latency event.
+     */
+    public interface StatsLatencyLogger {
+
+        enum LatencyType {
+            UNKNOWN(0),
+            COLD(1),
+            HOT(2);
+
+            private final int mId;
+
+            LatencyType(int id) {
+                this.mId = id;
+            }
+
+            public int getId() {
+                return mId;
+            }
+
+        }
+
+        /**
+         * Sets {@link InstanceId} of log message.
+         */
+        default StatsLatencyLogger withInstanceId(InstanceId instanceId) {
+            return this;
+        }
+
+
+        /**
+         * Sets latency of the event.
+         */
+        default StatsLatencyLogger withLatency(long latencyInMillis) {
+            return this;
+        }
+
+        /**
+         * Sets {@link LatencyType} of log message.
+         */
+        default StatsLatencyLogger withType(LatencyType type) {
+            return this;
+        }
+
+        /**
+         * Sets packageId of log message.
+         */
+        default StatsLatencyLogger withPackageId(int packageId) {
+            return this;
+        }
+
+        /**
+         * Builds the final message and logs it as {@link EventEnum}.
+         */
+        default void log(EventEnum event) {
+        }
+    }
+
+    /**
      * Returns new logger object.
      */
     public StatsLogger logger() {
@@ -672,11 +755,27 @@
         return logger;
     }
 
+    /**
+     * Returns new latency logger object.
+     */
+    public StatsLatencyLogger latencyLogger() {
+        StatsLatencyLogger logger = createLatencyLogger();
+        if (mInstanceId != null) {
+            logger.withInstanceId(mInstanceId);
+        }
+        return logger;
+    }
+
     protected StatsLogger createLogger() {
         return new StatsLogger() {
         };
     }
 
+    protected StatsLatencyLogger createLatencyLogger() {
+        return new StatsLatencyLogger() {
+        };
+    }
+
     /**
      * Sets InstanceId to every new {@link StatsLogger} object returned by {@link #logger()} when
      * not-null.
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 3e49d79..35fcb78 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -43,15 +43,18 @@
     public static final String KEY_WORKSPACE_SIZE = "migration_src_workspace_size";
     public static final String KEY_HOTSEAT_COUNT = "migration_src_hotseat_count";
     public static final String KEY_DEVICE_TYPE = "migration_src_device_type";
+    public static final String KEY_DB_FILE = "migration_src_db_file";
 
     private final String mGridSizeString;
     private final int mNumHotseat;
     private final @DeviceType int mDeviceType;
+    private final String mDbFile;
 
     public DeviceGridState(InvariantDeviceProfile idp) {
         mGridSizeString = String.format(Locale.ENGLISH, "%d,%d", idp.numColumns, idp.numRows);
         mNumHotseat = idp.numDatabaseHotseatIcons;
         mDeviceType = idp.deviceType;
+        mDbFile = idp.dbFile;
     }
 
     public DeviceGridState(Context context) {
@@ -59,6 +62,7 @@
         mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, "");
         mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1);
         mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE);
+        mDbFile = prefs.getString(KEY_DB_FILE, "");
     }
 
     /**
@@ -69,6 +73,20 @@
     }
 
     /**
+     * Returns the databaseFile for the grid.
+     */
+    public String getDbFile() {
+        return mDbFile;
+    }
+
+    /**
+     * Returns the number of hotseat icons.
+     */
+    public int getNumHotseat() {
+        return mNumHotseat;
+    }
+
+    /**
      * Stores the device state to shared preferences
      */
     public void writeToPrefs(Context context) {
@@ -76,6 +94,7 @@
                 .putString(KEY_WORKSPACE_SIZE, mGridSizeString)
                 .putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
                 .putInt(KEY_DEVICE_TYPE, mDeviceType)
+                .putString(KEY_DB_FILE, mDbFile)
                 .apply();
     }
 
@@ -106,6 +125,7 @@
                 + "mGridSizeString='" + mGridSizeString + '\''
                 + ", mNumHotseat=" + mNumHotseat
                 + ", mDeviceType=" + mDeviceType
+                + ", mDbFile=" + mDbFile
                 + '}';
     }
 
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 74b0a6f..e36d4cf 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -22,7 +22,6 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
@@ -104,13 +103,16 @@
      * Check given a new IDP, if migration is necessary.
      */
     public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
-        DeviceGridState idpGridState = new DeviceGridState(idp);
-        DeviceGridState contextGridState = new DeviceGridState(context);
-        boolean needsToMigrate = !idpGridState.isCompatible(contextGridState);
+        return needsToMigrate(new DeviceGridState(context), new DeviceGridState(idp));
+    }
+
+    private static boolean needsToMigrate(
+            DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
+        boolean needsToMigrate = !destDeviceState.isCompatible(srcDeviceState);
         // TODO(b/198965093): Revert this change after bug is fixed
         if (needsToMigrate) {
-            Log.d("b/198965093", "Migration is needed. idpGridState: " + idpGridState
-                    + ", contextGridState: " + contextGridState);
+            Log.d("b/198965093", "Migration is needed. destDeviceState: " + destDeviceState
+                    + ", srcDeviceState: " + srcDeviceState);
         }
         return needsToMigrate;
     }
@@ -143,23 +145,26 @@
             idp = LauncherAppState.getIDP(context);
         }
 
-        if (!needsToMigrate(context, idp)) {
+        DeviceGridState srcDeviceState = new DeviceGridState(context);
+        DeviceGridState destDeviceState = new DeviceGridState(idp);
+        if (!needsToMigrate(srcDeviceState, destDeviceState)) {
             return true;
         }
 
-        SharedPreferences prefs = Utilities.getPrefs(context);
         HashSet<String> validPackages = getValidPackages(context);
 
         if (migrateForPreview) {
             if (!LauncherSettings.Settings.call(
                     context.getContentResolver(),
-                    LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW, idp.dbFile).getBoolean(
+                    LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW,
+                    destDeviceState.getDbFile()).getBoolean(
                     LauncherSettings.Settings.EXTRA_VALUE)) {
                 return false;
             }
         } else if (!LauncherSettings.Settings.call(
                 context.getContentResolver(),
-                LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
+                LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER,
+                destDeviceState.getDbFile()).getBoolean(
                 LauncherSettings.Settings.EXTRA_VALUE)) {
             return false;
         }
@@ -179,10 +184,10 @@
                             : LauncherSettings.Favorites.TABLE_NAME,
                     context, validPackages);
 
-            Point targetSize = new Point(idp.numColumns, idp.numRows);
+            Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
             GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
-                    srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
-            task.migrate(idp);
+                    srcReader, destReader, destDeviceState.getNumHotseat(), targetSize);
+            task.migrate(srcDeviceState, destDeviceState);
 
             if (!migrateForPreview) {
                 dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -200,13 +205,13 @@
 
             if (!migrateForPreview) {
                 // Save current configuration, so that the migration does not run again.
-                new DeviceGridState(idp).writeToPrefs(context);
+                destDeviceState.writeToPrefs(context);
             }
         }
     }
 
     @VisibleForTesting
-    protected boolean migrate(InvariantDeviceProfile idp) {
+    protected boolean migrate(DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
         if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
             return false;
         }
@@ -228,8 +233,6 @@
 
         boolean preservePages = false;
         if (screens.isEmpty() && FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.get()) {
-            DeviceGridState srcDeviceState = new DeviceGridState(mContext);
-            DeviceGridState destDeviceState = new DeviceGridState(idp);
             preservePages = destDeviceState.compareTo(srcDeviceState) >= 0
                     && destDeviceState.getColumns() - srcDeviceState.getColumns() <= 2;
         }
@@ -392,7 +395,8 @@
             mTrgX = trgX;
             mTrgY = trgY;
             mNextStartX = 0;
-            mNextStartY = mTrgY - 1;
+            mNextStartY = mScreenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
+                    ? 1 /* smartspace */ : 0;
             List<DbEntry> existedEntries = mDestReader.mWorkspaceEntriesByScreenId.get(screenId);
             if (existedEntries != null) {
                 for (DbEntry entry : existedEntries) {
@@ -427,7 +431,7 @@
          * to speed up the search.
          */
         private boolean findPlacement(DbEntry entry) {
-            for (int y = mNextStartY; y >= (mScreenId == 0 ? 1 /* smartspace */ : 0); y--) {
+            for (int y = mNextStartY; y <  mTrgY; y++) {
                 for (int x = mNextStartX; x < mTrgX; x++) {
                     boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
                     boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
@@ -750,7 +754,7 @@
                 return Integer.compare(screenId, another.screenId);
             }
             if (cellY != another.cellY) {
-                return -Integer.compare(cellY, another.cellY);
+                return Integer.compare(cellY, another.cellY);
             }
             return Integer.compare(cellX, another.cellX);
         }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index b9fa21d..f1c5d59 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -615,7 +615,13 @@
                             }
 
                             if (info != null) {
-                                iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon));
+                                if (info.itemType
+                                        != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                                    // Skip deep shortcuts; their title and icons have already been
+                                    // loaded above.
+                                    iconRequestInfos.add(
+                                            c.createIconRequestInfo(info, useLowResIcon));
+                                }
 
                                 c.applyCommonProperties(info);
 
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index cc42258..3bd9470 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -90,10 +90,9 @@
      */
     @WorkerThread
     public void loadStringCache(StringCache cache) {
-        cache.loadDefaultStrings(mContext);
+        cache.loadStrings(mContext);
     }
 
-
     /**
      * Called during loader after workspace loading is complete
      */
diff --git a/src/com/android/launcher3/model/StringCache.java b/src/com/android/launcher3/model/StringCache.java
index 11d3e70..2fc852d 100644
--- a/src/com/android/launcher3/model/StringCache.java
+++ b/src/com/android/launcher3/model/StringCache.java
@@ -16,16 +16,100 @@
 
 package com.android.launcher3.model;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 
 /**
  *
- * Cache for some of the string used in Launcher.
+ * Cache for the device policy strings used in Launcher.
  */
 public class StringCache {
 
+    private static final String PREFIX = "Launcher.";
+
+    /**
+     * User on-boarding title for work profile apps.
+     */
+    private static final String WORK_PROFILE_EDU = PREFIX + "WORK_PROFILE_EDU";
+
+    /**
+     * Action label to finish work profile edu.
+     */
+    private static final String WORK_PROFILE_EDU_ACCEPT = PREFIX + "WORK_PROFILE_EDU_ACCEPT";
+
+    /**
+     * Title shown when user opens work apps tab while work profile is paused.
+     */
+    private static final String WORK_PROFILE_PAUSED_TITLE =
+            PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+    /**
+     * Description shown when user opens work apps tab while work profile is paused.
+     */
+    private static final String WORK_PROFILE_PAUSED_DESCRIPTION =
+            PREFIX + "WORK_PROFILE_PAUSED_DESCRIPTION";
+
+    /**
+     * Shown on the button to pause work profile.
+     */
+    private static final String WORK_PROFILE_PAUSE_BUTTON =
+            PREFIX + "WORK_PROFILE_PAUSE_BUTTON";
+
+    /**
+     * Shown on the button to enable work profile.
+     */
+    private static final String WORK_PROFILE_ENABLE_BUTTON =
+            PREFIX + "WORK_PROFILE_ENABLE_BUTTON";
+
+    /**
+     * Label on launcher tab to indicate work apps.
+     */
+    private static final String ALL_APPS_WORK_TAB = PREFIX + "ALL_APPS_WORK_TAB";
+
+    /**
+     * Label on launcher tab to indicate personal apps.
+     */
+    private static final String ALL_APPS_PERSONAL_TAB = PREFIX + "ALL_APPS_PERSONAL_TAB";
+
+    /**
+     * Accessibility description for launcher tab to indicate work apps.
+     */
+    private static final String ALL_APPS_WORK_TAB_ACCESSIBILITY =
+            PREFIX + "ALL_APPS_WORK_TAB_ACCESSIBILITY";
+
+    /**
+     * Accessibility description for launcher tab to indicate personal apps.
+     */
+    private static final String ALL_APPS_PERSONAL_TAB_ACCESSIBILITY =
+            PREFIX + "ALL_APPS_PERSONAL_TAB_ACCESSIBILITY";
+
+    /**
+     * Work folder name.
+     */
+    private static final String WORK_FOLDER_NAME = PREFIX + "WORK_FOLDER_NAME";
+
+    /**
+     * Label on widget tab to indicate work app widgets.
+     */
+    private static final String WIDGETS_WORK_TAB = PREFIX + "WIDGETS_WORK_TAB";
+
+    /**
+     * Label on widget tab to indicate personal app widgets.
+     */
+    private static final String WIDGETS_PERSONAL_TAB = PREFIX + "WIDGETS_PERSONAL_TAB";
+
+    /**
+     * Message shown when a feature is disabled by the admin (e.g. changing wallpaper).
+     */
+    private static final String DISABLED_BY_ADMIN_MESSAGE =
+            PREFIX + "DISABLED_BY_ADMIN_MESSAGE";
+
     /**
      * User on-boarding title for work profile apps.
      */
@@ -99,22 +183,51 @@
     /**
      * Sets the default values for the strings.
      */
-    public void loadDefaultStrings(Context context) {
-        workProfileEdu = context.getString(R.string.work_profile_edu_work_apps);
-        workProfileEduAccept = context.getString(R.string.work_profile_edu_accept);
-        workProfilePausedTitle = context.getString(R.string.work_apps_paused_title);
-        workProfilePausedDescription = context.getString(R.string.work_apps_paused_body);
-        workProfilePauseButton = context.getString(R.string.work_apps_pause_btn_text);
-        workProfileEnableButton = context.getString(R.string.work_apps_enable_btn_text);
-        allAppsWorkTab = context.getString(R.string.all_apps_work_tab);
-        allAppsPersonalTab = context.getString(R.string.all_apps_personal_tab);
-        allAppsWorkTabAccessibility = context.getString(R.string.all_apps_button_work_label);
-        allAppsPersonalTabAccessibility = context.getString(
+    public void loadStrings(Context context) {
+        workProfileEdu = getEnterpriseString(
+                context, WORK_PROFILE_EDU, R.string.work_profile_edu_work_apps);
+        workProfileEduAccept = getEnterpriseString(
+                context, WORK_PROFILE_EDU_ACCEPT, R.string.work_profile_edu_accept);
+        workProfilePausedTitle = getEnterpriseString(
+                context, WORK_PROFILE_PAUSED_TITLE, R.string.work_apps_paused_title);
+        workProfilePausedDescription = getEnterpriseString(
+                context, WORK_PROFILE_PAUSED_DESCRIPTION, R.string.work_apps_paused_body);
+        workProfilePauseButton = getEnterpriseString(
+                context, WORK_PROFILE_PAUSE_BUTTON, R.string.work_apps_pause_btn_text);
+        workProfileEnableButton = getEnterpriseString(
+                context, WORK_PROFILE_ENABLE_BUTTON, R.string.work_apps_enable_btn_text);
+        allAppsWorkTab = getEnterpriseString(
+                context, ALL_APPS_WORK_TAB, R.string.all_apps_work_tab);
+        allAppsPersonalTab = getEnterpriseString(
+                context, ALL_APPS_PERSONAL_TAB, R.string.all_apps_personal_tab);
+        allAppsWorkTabAccessibility = getEnterpriseString(
+                context, ALL_APPS_WORK_TAB_ACCESSIBILITY, R.string.all_apps_button_work_label);
+        allAppsPersonalTabAccessibility = getEnterpriseString(
+                context, ALL_APPS_PERSONAL_TAB_ACCESSIBILITY,
                 R.string.all_apps_button_personal_label);
-        workFolderName = context.getString(R.string.work_folder_name);
-        widgetsWorkTab = context.getString(R.string.widgets_full_sheet_work_tab);
-        widgetsPersonalTab = context.getString(R.string.widgets_full_sheet_personal_tab);
-        disabledByAdminMessage = context.getString(R.string.msg_disabled_by_admin);
+        workFolderName = getEnterpriseString(
+                context, WORK_FOLDER_NAME, R.string.work_folder_name);
+        widgetsWorkTab = getEnterpriseString(
+                context, WIDGETS_WORK_TAB, R.string.widgets_full_sheet_work_tab);
+        widgetsPersonalTab = getEnterpriseString(
+                context, WIDGETS_PERSONAL_TAB, R.string.widgets_full_sheet_personal_tab);
+        disabledByAdminMessage = getEnterpriseString(
+                context, DISABLED_BY_ADMIN_MESSAGE, R.string.msg_disabled_by_admin);
+    }
+
+    private String getEnterpriseString(
+            Context context, String updatableStringId, int defaultStringId) {
+        return Utilities.ATLEAST_T
+                ? getUpdatableEnterpriseSting(context, updatableStringId, defaultStringId)
+                : context.getString(defaultStringId);
+    }
+
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+    private String getUpdatableEnterpriseSting(
+            Context context, String updatableStringId, int defaultStringId) {
+        DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        return dpm.getResources().getString(
+                updatableStringId, () -> context.getString(defaultStringId));
     }
 
     @Override
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index cd2ef35..efebce3 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -217,7 +217,7 @@
         return getDefaultItemInfoBuilder()
                 .setFolderIcon(folderIcon)
                 .setRank(rank)
-                .setAttribute(getLabelState().mLogAttribute)
+                .addItemAttributes(getLabelState().mLogAttribute)
                 .setContainerInfo(getContainerInfo())
                 .build();
     }
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index 0283d5f..e57a895 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -288,7 +288,7 @@
         LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
         return info.toBuilder()
                 .setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures))
-                .setAttribute(getAttribute(sourceContainer))
+                .addItemAttributes(getAttribute(sourceContainer))
                 .build();
     }
 }
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index ebe45a5..bb2c37f 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.notification;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ACTION_POPUP;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_LAUNCH_TAP;
 
 import android.app.ActivityOptions;
@@ -116,7 +118,8 @@
                 popupDataProvider.cancelNotification(notificationKey);
             }
         }
-        AbstractFloatingView.closeOpenContainer(context, AbstractFloatingView.TYPE_ACTION_POPUP);
+        AbstractFloatingView.closeOpenViews(
+                context, true, TYPE_ACTION_POPUP | TYPE_TASKBAR_ALL_APPS);
     }
 
     public Drawable getIconForBackground(Context context, int background) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 05d6fc6..2fa7945 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -508,7 +508,9 @@
     @Override
     protected void closeComplete() {
         super.closeComplete();
-        mActivityContext.getDragController().removeDragListener(this);
+        if (mActivityContext.getDragController() != null) {
+            mActivityContext.getDragController().removeDragListener(this);
+        }
         PopupContainerWithArrow openPopup = getOpen(mActivityContext);
         if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
             mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 967f2c8..9201006 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -113,13 +113,17 @@
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             if (child == mAppsView) {
-                int padding = 2 * (grid.desiredWorkspaceHorizontalMarginPx
-                        + grid.cellLayoutPaddingLeftRightPx);
+                int horizontalPadding = (2 * grid.desiredWorkspaceHorizontalMarginPx)
+                        + grid.cellLayoutPaddingPx.left + grid.cellLayoutPaddingPx.right;
+                int verticalPadding =
+                        grid.cellLayoutPaddingPx.top + grid.cellLayoutPaddingPx.bottom;
 
-                int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding;
+                int maxWidth =
+                        grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding;
                 int appsWidth = Math.min(width, maxWidth);
 
-                int maxHeight = grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + padding;
+                int maxHeight =
+                        grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding;
                 int appsHeight = Math.min(height, maxHeight);
 
                 mAppsView.measure(
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index b45c97b..c554d06 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -17,15 +17,11 @@
 
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 
-import android.graphics.Insets;
-import android.os.Build;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.WindowInsets;
 
 import androidx.annotation.CallSuper;
-import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.LauncherRootView;
@@ -179,14 +175,6 @@
     }
 
     /**
-     * Gives subclasses a chance to override some window insets (via
-     * {@link android.view.WindowInsets.Builder#setInsets(int, Insets)}).
-     */
-    @RequiresApi(api = Build.VERSION_CODES.R)
-    public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
-            WindowInsets oldInsets) { }
-
-    /**
      * Runs the given {@param r} runnable when this activity binds to the touch interaction service.
      */
     public void runOnBindToTouchInteractionService(Runnable r) {
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 867fd99..8b425da 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -21,7 +21,7 @@
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 
 import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
+import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index d52594e..9be3cc5 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -18,7 +18,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 
 import android.content.Context;
-import android.graphics.Rect;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -52,28 +51,15 @@
             return super.getWorkspaceScaleAndTranslation(launcher);
         }
 
-        if (grid.isVerticalBarLayout()) {
-            float scale = grid.workspaceSpringLoadShrinkFactor;
-            return new ScaleAndTranslation(scale, 0, 0);
-        }
-
-        float scale = grid.workspaceSpringLoadShrinkFactor;
-        Rect insets = launcher.getDragLayer().getInsets();
-
-        float scaledHeight = scale * ws.getNormalChildHeight();
-        float shrunkTop = insets.top + grid.dropTargetBarSizePx;
-        float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
-                - grid.workspacePadding.bottom
-                - grid.workspaceSpringLoadedBottomSpace;
-        float totalShrunkSpace = shrunkBottom - shrunkTop;
-
-        float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
+        float shrunkTop = grid.getWorkspaceSpringLoadShrunkTop();
+        float shrunkBottom = grid.getWorkspaceSpringLoadShrunkBottom();
+        float scale = (shrunkBottom - shrunkTop) / ws.getNormalChildHeight();
 
         float halfHeight = ws.getHeight() / 2;
         float myCenter = ws.getTop() + halfHeight;
         float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
         float actualCellTop = myCenter - cellTopFromCenter * scale;
-        return new ScaleAndTranslation(scale, 0, (desiredCellTop - actualCellTop) / scale);
+        return new ScaleAndTranslation(scale, 0, (shrunkTop - actualCellTop) / scale);
     }
 
     @Override
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index bd6f7d3..f04e685 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -62,6 +62,7 @@
             ANIM_OVERVIEW_MODAL,
             ANIM_DEPTH,
             ANIM_OVERVIEW_ACTIONS_FADE,
+            ANIM_WORKSPACE_PAGE_TRANSLATE_X,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimType {}
@@ -80,8 +81,9 @@
     public static final int ANIM_OVERVIEW_MODAL = 12;
     public static final int ANIM_DEPTH = 13;
     public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14;
+    public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
 
-    private static final int ANIM_TYPES_COUNT = 15;
+    private static final int ANIM_TYPES_COUNT = 16;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 2bf3b14..faf5817 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -82,6 +82,10 @@
     public static final String REQUEST_IS_LAUNCHER_INITIALIZED = "is-launcher-initialized";
     public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
     public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
+    public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing";
+    public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing";
+    public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
+    public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
     public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
     public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
     public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
@@ -96,6 +100,10 @@
     public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
     public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
     public static final String REQUEST_CLEAR_DATA = "clear-data";
+    public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
+    public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
+            "use-default-workspace-layout";
+    public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
     public static final String REQUEST_IS_TABLET = "is-tablet";
     public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
     public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
@@ -125,8 +133,9 @@
     public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
 
     public static final String PERMANENT_DIAG_TAG = "TaplTarget";
-    public static final String TASK_VIEW_ID_CRASH = "b/195430732";
     public static final String NO_DROP_TARGET = "b/195031154";
     public static final String NULL_INT_SET = "b/200572078";
     public static final String MISSING_PROMISE_ICON = "b/202985412";
+
+    public static final String BAD_STATE = "b/223498680";
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 61d488c..c00e174 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -26,7 +26,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator.AnimatorListener;
 import android.animation.ValueAnimator;
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 4a55d2e..4fcba18 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.touch;
 
+import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.Gravity.END;
 import static android.view.Gravity.START;
@@ -178,18 +179,6 @@
     }
 
     @Override
-    public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
-            DeviceProfile dp) {
-        // Don't use device profile here because we know we're in fake landscape, only split option
-        // available is top/left
-        if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
-            // Top (visually left) side
-            return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
-        }
-        throw new IllegalStateException("Invalid split stage position: " + stagePosition);
-    }
-
-    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollY();
     }
@@ -405,12 +394,21 @@
     }
 
     @Override
-    public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
-            @StagePosition int stagePosition, Rect out) {
+    public void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset,
+            DeviceProfile dp, @StagePosition int stagePosition, Rect out) {
         // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
         // which is the same bounds as 0 rotation.
         int width = dp.widthPx;
         out.set(0, 0, width, placeholderHeight);
+        out.inset(placeholderInset, 0);
+
+        // Adjust the top to account for content off screen. This will help to animate the view in
+        // with rounded corners.
+        int screenWidth = dp.widthPx;
+        int screenHeight = dp.heightPx;
+        int totalHeight = (int) (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset)
+                / screenWidth);
+        out.top -= (totalHeight - placeholderHeight);
     }
 
     @Override
@@ -426,14 +424,17 @@
     @Override
     public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
             StagedSplitBounds splitInfo, int desiredStagePosition) {
-        float diff;
-        float horizontalDividerDiff = splitInfo.visualDividerBounds.width() / 2f;
+        float topLeftTaskPercent = splitInfo.appsStackedVertically
+                ? splitInfo.topTaskPercent
+                : splitInfo.leftTaskPercent;
+        float dividerBarPercent = splitInfo.appsStackedVertically
+                ? splitInfo.dividerHeightPercent
+                : splitInfo.dividerWidthPercent;
+
         if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
-            diff = outRect.height() * (1f - splitInfo.leftTaskPercent) + horizontalDividerDiff;
-            outRect.bottom -= diff;
+            outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
         } else {
-            diff = outRect.height() * splitInfo.leftTaskPercent + horizontalDividerDiff;
-            outRect.top += diff;
+            outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
         }
     }
 
@@ -469,31 +470,53 @@
     }
 
     @Override
-    public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
-        FrameLayout.LayoutParams iconParams =
-                (FrameLayout.LayoutParams) iconView.getLayoutParams();
+    public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
+            int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
         iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
         iconParams.leftMargin = 0;
-        iconParams.topMargin = snapshotParams.topMargin / 2;
+        iconParams.topMargin = thumbnailTopMargin / 2;
     }
 
     @Override
     public void setSplitIconParams(View primaryIconView, View secondaryIconView,
             int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
-            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+            int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
+            DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
         FrameLayout.LayoutParams primaryIconParams =
                 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
         FrameLayout.LayoutParams secondaryIconParams =
                 new FrameLayout.LayoutParams(primaryIconParams);
 
-        primaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? START : END);
+        // We calculate the "midpoint" of the thumbnail area, and place the icons there.
+        // This is the place where the thumbnail area splits by default, in a near-50/50 split.
+        // It is usually not exactly 50/50, due to insets/screen cutouts.
+        int fullscreenInsetThickness = deviceProfile.getInsets().top;
+        int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness)
+                / 2);
+        float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
+        float insetPct = (float) fullscreenInsetThickness / deviceProfile.heightPx;
+        int spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx;
+        int overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots;
+        int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness * midpointFromBottomPct);
+        int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct);
+
+        primaryIconParams.gravity = BOTTOM | (isRtl ? START : END);
+        secondaryIconParams.gravity = BOTTOM | (isRtl ? START : END);
         primaryIconView.setTranslationX(0);
-        primaryIconView.setTranslationY(-(taskIconHeight / 2f));
-        secondaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? START : END);
         secondaryIconView.setTranslationX(0);
-        secondaryIconView.setTranslationY(taskIconHeight / 2f);
+        if (splitConfig.initiatedFromSeascape) {
+            // if the split was initiated from seascape,
+            // the task on the right (secondary) is slightly larger
+            primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
+            secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
+                    + taskIconHeight);
+        } else {
+            // if not,
+            // the task on the left (primary) is slightly larger
+            primaryIconView.setTranslationY(-bottomToMidpointOffset);
+            secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+        }
 
         primaryIconView.setLayoutParams(primaryIconParams);
         secondaryIconView.setLayoutParams(secondaryIconParams);
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 923dcc6..850eaaf 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -47,10 +47,6 @@
  */
 public interface PagedOrientationHandler {
 
-    int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
-    int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
-    int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
-
     PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
     PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
     PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -82,12 +78,6 @@
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
 
-    /**
-     * @param stagePosition The position where the view to be split will go
-     * @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
-     * dismissal should happen
-     */
-    int getSplitTaskViewDismissDirection(@StagePosition int stagePosition, DeviceProfile dp);
     int getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
@@ -120,10 +110,10 @@
     int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
     List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
     /**
-     * @param splitholderSize height of placeholder view in portrait, width in landscape
+     * @param placeholderHeight height of placeholder view in portrait, width in landscape
      */
-    void getInitialSplitPlaceholderBounds(int splitholderSize, DeviceProfile dp,
-            @StagePosition int stagePosition, Rect out);
+    void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset,
+            DeviceProfile dp, @StagePosition int stagePosition, Rect out);
 
     /**
      * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
@@ -152,11 +142,12 @@
             StagedSplitBounds splitBoundsConfig, DeviceProfile dp);
 
     // Overview TaskMenuView methods
-    void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl);
+    void setTaskIconParams(FrameLayout.LayoutParams iconParams,
+            int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl);
     void setSplitIconParams(View primaryIconView, View secondaryIconView,
             int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
-            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
+            int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
+            DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
 
     /*
      * The following two methods try to center the TaskMenuView in landscape by finding the center
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 0d92e25..d132901 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -18,6 +18,7 @@
 
 import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.END;
 import static android.view.Gravity.START;
 import static android.view.Gravity.TOP;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -180,24 +181,6 @@
     }
 
     @Override
-    public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
-            DeviceProfile dp) {
-        if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
-            if (dp.isLandscape) {
-                // Left side
-                return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
-            } else {
-                // Top side
-                return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
-            }
-        } else if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
-            // We don't have a bottom option, so should be right
-            return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
-        }
-        throw new IllegalStateException("Invalid split stage position: " + stagePosition);
-    }
-
-    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollX();
     }
@@ -352,8 +335,14 @@
         // Set translations
         if (deviceProfile.isLandscape) {
             if (desiredTaskId == splitBounds.rightBottomTaskId) {
-                translationX = ((taskViewWidth * splitBounds.leftTaskPercent)
-                                + (taskViewWidth * splitBounds.dividerWidthPercent));
+                float leftTopTaskPercent = splitBounds.appsStackedVertically
+                        ? splitBounds.topTaskPercent
+                        : splitBounds.leftTaskPercent;
+                float dividerThicknessPercent = splitBounds.appsStackedVertically
+                        ? splitBounds.dividerHeightPercent
+                        : splitBounds.dividerWidthPercent;
+                translationX = ((taskViewWidth * leftTopTaskPercent)
+                        + (taskViewWidth * dividerThicknessPercent));
             }
         } else {
             if (desiredTaskId == splitBounds.leftTopTaskId) {
@@ -447,29 +436,48 @@
     }
 
     @Override
-    public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
-            @StagePosition int stagePosition, Rect out) {
-        int width = dp.widthPx;
-        out.set(0, 0, width, placeholderHeight);
+    public void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset,
+            DeviceProfile dp, @StagePosition int stagePosition, Rect out) {
+        int screenWidth = dp.widthPx;
+        int screenHeight = dp.heightPx;
+        out.set(0, 0, screenWidth, placeholderHeight);
         if (!dp.isLandscape) {
             // portrait, phone or tablet - spans width of screen, nothing else to do
+            out.inset(placeholderInset, 0);
+
+            // Adjust the top to account for content off screen. This will help to animate the view
+            // in with rounded corners.
+            int totalHeight = (int) (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset)
+                    / screenWidth);
+            out.top -= (totalHeight - placeholderHeight);
             return;
         }
 
         // Now we rotate the portrait rect depending on what side we want pinned
         boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
 
-        int screenHeight = dp.heightPx;
-        float postRotateScale = (float) screenHeight / width;
+        float postRotateScale = (float) screenHeight / screenWidth;
         mTmpMatrix.reset();
         mTmpMatrix.postRotate(pinToRight ? 90 : 270);
-        mTmpMatrix.postTranslate(pinToRight ? width : 0, pinToRight ? 0 : width);
+        mTmpMatrix.postTranslate(pinToRight ? screenWidth : 0, pinToRight ? 0 : screenWidth);
         // The placeholder height stays constant after rotation, so we don't change width scale
         mTmpMatrix.postScale(1, postRotateScale);
 
         mTmpRectF.set(out);
         mTmpMatrix.mapRect(mTmpRectF);
+        mTmpRectF.inset(0, placeholderInset);
         mTmpRectF.roundOut(out);
+
+        // Adjust the top to account for content off screen. This will help to animate the view in
+        // with rounded corners.
+        int totalWidth = (int) (1.0f * screenWidth / 2 * (screenHeight - 2 * placeholderInset)
+                / screenHeight);
+        int width = out.width();
+        if (pinToRight) {
+            out.right += totalWidth - width;
+        } else {
+            out.left -= totalWidth - width;
+        }
     }
 
     @Override
@@ -506,19 +514,24 @@
     public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
             StagedSplitBounds splitInfo, int desiredStagePosition) {
         boolean isLandscape = dp.isLandscape;
+        float topLeftTaskPercent = splitInfo.appsStackedVertically
+                ? splitInfo.topTaskPercent
+                : splitInfo.leftTaskPercent;
+        float dividerBarPercent = splitInfo.appsStackedVertically
+                ? splitInfo.dividerHeightPercent
+                : splitInfo.dividerWidthPercent;
+
         if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
             if (isLandscape) {
-                outRect.right = outRect.left + (int) (outRect.width() * splitInfo.leftTaskPercent);
+                outRect.right = outRect.left + (int) (outRect.width() * topLeftTaskPercent);
             } else {
-                outRect.bottom = outRect.top + (int) (outRect.height() * splitInfo.topTaskPercent);
+                outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
             }
         } else {
             if (isLandscape) {
-                outRect.left += (int) (outRect.width() *
-                        (splitInfo.leftTaskPercent + splitInfo.dividerWidthPercent));
+                outRect.left += (int) (outRect.width() * (topLeftTaskPercent + dividerBarPercent));
             } else {
-                outRect.top += (int) (outRect.height() *
-                        (splitInfo.topTaskPercent + splitInfo.dividerHeightPercent));
+                outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
             }
         }
     }
@@ -567,10 +580,8 @@
     }
 
     @Override
-    public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
-        FrameLayout.LayoutParams iconParams =
-                (FrameLayout.LayoutParams) iconView.getLayoutParams();
+    public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
+            int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = TOP | CENTER_HORIZONTAL;
         iconParams.leftMargin = iconParams.rightMargin = 0;
         iconParams.topMargin = taskIconMargin;
@@ -579,18 +590,70 @@
     @Override
     public void setSplitIconParams(View primaryIconView, View secondaryIconView,
             int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
-            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+            int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
+            DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
         FrameLayout.LayoutParams primaryIconParams =
                 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
         FrameLayout.LayoutParams secondaryIconParams =
                 new FrameLayout.LayoutParams(primaryIconParams);
 
-        primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
-        // shifts icon half a width left (height is used conveniently here since icons are square)
-        primaryIconView.setTranslationX(-(taskIconHeight / 2f));
+        if (deviceProfile.isLandscape) {
+            // We calculate the "midpoint" of the thumbnail area, and place the icons there.
+            // This is the place where the thumbnail area splits by default, in a near-50/50 split.
+            // It is usually not exactly 50/50, due to insets/screen cutouts.
+            int fullscreenInsetThickness = deviceProfile.isSeascape()
+                    ? deviceProfile.getInsets().right
+                    : deviceProfile.getInsets().left;
+            int fullscreenMidpointFromBottom = ((deviceProfile.widthPx
+                    - fullscreenInsetThickness) / 2);
+            float midpointFromBottomPct = (float) fullscreenMidpointFromBottom
+                    / deviceProfile.widthPx;
+            float insetPct = (float) fullscreenInsetThickness / deviceProfile.widthPx;
+            int spaceAboveSnapshots = 0;
+            int overviewThumbnailAreaThickness = groupedTaskViewWidth - spaceAboveSnapshots;
+            int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness
+                    * midpointFromBottomPct);
+            int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct);
+
+            if (deviceProfile.isSeascape()) {
+                primaryIconParams.gravity = TOP | (isRtl ? END : START);
+                secondaryIconParams.gravity = TOP | (isRtl ? END : START);
+                if (splitConfig.initiatedFromSeascape) {
+                    // if the split was initiated from seascape,
+                    // the task on the right (secondary) is slightly larger
+                    primaryIconView.setTranslationX(bottomToMidpointOffset - taskIconHeight);
+                    secondaryIconView.setTranslationX(bottomToMidpointOffset);
+                } else {
+                    // if not,
+                    // the task on the left (primary) is slightly larger
+                    primaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset
+                            - taskIconHeight);
+                    secondaryIconView.setTranslationX(bottomToMidpointOffset + insetOffset);
+                }
+            } else {
+                primaryIconParams.gravity = TOP | (isRtl ? START : END);
+                secondaryIconParams.gravity = TOP | (isRtl ? START : END);
+                if (!splitConfig.initiatedFromSeascape) {
+                    // if the split was initiated from landscape,
+                    // the task on the left (primary) is slightly larger
+                    primaryIconView.setTranslationX(-bottomToMidpointOffset);
+                    secondaryIconView.setTranslationX(-bottomToMidpointOffset + taskIconHeight);
+                } else {
+                    // if not,
+                    // the task on the right (secondary) is slightly larger
+                    primaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset);
+                    secondaryIconView.setTranslationX(-bottomToMidpointOffset - insetOffset
+                            + taskIconHeight);
+                }
+            }
+        } else {
+            primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
+            // shifts icon half a width left (height is used here since icons are square)
+            primaryIconView.setTranslationX(-(taskIconHeight / 2f));
+            secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
+            secondaryIconView.setTranslationX(taskIconHeight / 2f);
+        }
         primaryIconView.setTranslationY(0);
-        secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
-        secondaryIconView.setTranslationX(taskIconHeight / 2f);
         secondaryIconView.setTranslationY(0);
 
         primaryIconView.setLayoutParams(primaryIconParams);
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 69e19f4..74b6a5b 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -23,7 +23,6 @@
 
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
@@ -168,29 +167,57 @@
     }
 
     @Override
-    public void setIconAndSnapshotParams(View mIconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
-        FrameLayout.LayoutParams iconParams =
-                (FrameLayout.LayoutParams) mIconView.getLayoutParams();
+    public void setTaskIconParams(FrameLayout.LayoutParams iconParams,
+            int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
         iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2;
         iconParams.rightMargin = 0;
-        iconParams.topMargin = snapshotParams.topMargin / 2;
+        iconParams.topMargin = thumbnailTopMargin / 2;
     }
 
     @Override
     public void setSplitIconParams(View primaryIconView, View secondaryIconView,
             int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
-            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+            int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
+            DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
         super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight,
-                primarySnapshotWidth, primarySnapshotHeight, isRtl, deviceProfile, splitConfig);
+                primarySnapshotWidth, primarySnapshotHeight, groupedTaskViewHeight,
+                groupedTaskViewWidth, isRtl, deviceProfile, splitConfig);
         FrameLayout.LayoutParams primaryIconParams =
                 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
         FrameLayout.LayoutParams secondaryIconParams =
                 (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams();
 
-        primaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? END : START);
-        secondaryIconParams.gravity = CENTER_VERTICAL | (isRtl ? END : START);
+        // We calculate the "midpoint" of the thumbnail area, and place the icons there.
+        // This is the place where the thumbnail area splits by default, in a near-50/50 split.
+        // It is usually not exactly 50/50, due to insets/screen cutouts.
+        int fullscreenInsetThickness = deviceProfile.getInsets().top;
+        int fullscreenMidpointFromBottom = ((deviceProfile.heightPx
+                - fullscreenInsetThickness) / 2);
+        float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
+        float insetPct = (float) fullscreenInsetThickness / deviceProfile.heightPx;
+        int spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx;
+        int overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots;
+        int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness * midpointFromBottomPct);
+        int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct);
+
+        primaryIconParams.gravity = BOTTOM | (isRtl ? END : START);
+        secondaryIconParams.gravity = BOTTOM | (isRtl ? END : START);
+        primaryIconView.setTranslationX(0);
+        secondaryIconView.setTranslationX(0);
+        if (splitConfig.initiatedFromSeascape) {
+            // if the split was initiated from seascape,
+            // the task on the right (secondary) is slightly larger
+            primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
+            secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
+                    + taskIconHeight);
+        } else {
+            // if not,
+            // the task on the left (primary) is slightly larger
+            primaryIconView.setTranslationY(-bottomToMidpointOffset);
+            secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+        }
+
         primaryIconView.setLayoutParams(primaryIconParams);
         secondaryIconView.setLayoutParams(secondaryIconParams);
     }
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index e8941e6..fe0bf6d 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -121,6 +121,9 @@
             mLongPressState = STATE_COMPLETED;
         }
 
+        boolean isInAllAppsBottomSheet = mLauncher.isInState(ALL_APPS)
+                && mLauncher.getDeviceProfile().isTablet;
+
         final boolean result;
         if (mLongPressState == STATE_COMPLETED) {
             // We have handled the touch, so workspace does not need to know anything anymore.
@@ -136,8 +139,9 @@
 
             result = true;
         } else {
-            // We don't want to handle touch, let workspace handle it as usual.
-            result = false;
+            // We don't want to handle touch unless we're in AllApps bottom sheet, let workspace
+            // handle it as usual.
+            result = isInAllAppsBottomSheet;
         }
 
         if (action == ACTION_UP || action == ACTION_POINTER_UP) {
@@ -153,9 +157,7 @@
         if (action == ACTION_UP || action == ACTION_CANCEL) {
             cancelLongPress();
         }
-        if (action == ACTION_UP
-                && mLauncher.isInState(ALL_APPS)
-                && mLauncher.getDeviceProfile().isTablet) {
+        if (action == ACTION_UP && isInAllAppsBottomSheet) {
             mLauncher.getStateManager().goToState(NORMAL);
             mLauncher.getStatsLogManager().logger()
                     .withSrcState(ALL_APPS.statsLogOrdinal)
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index ba925f5..8b4ff85 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -15,42 +15,48 @@
  */
 package com.android.launcher3.util;
 
+import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
-
-import static java.util.Collections.emptyMap;
+import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
+import static com.android.launcher3.util.window.WindowManagerProxy.MIN_LARGE_TABLET_WIDTH;
+import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Build;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Display;
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
+import com.android.launcher3.util.window.CachedDisplayInfo;
+import com.android.launcher3.util.window.WindowManagerProxy;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Map;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.Set;
 
@@ -58,7 +64,7 @@
  * Utility class to cache properties of default display to avoid a system RPC on every call.
  */
 @SuppressLint("NewApi")
-public class DisplayController implements DisplayListener, ComponentCallbacks, SafeCloseable {
+public class DisplayController implements ComponentCallbacks, SafeCloseable {
 
     private static final String TAG = "DisplayController";
 
@@ -69,19 +75,27 @@
     public static final int CHANGE_ROTATION = 1 << 1;
     public static final int CHANGE_DENSITY = 1 << 2;
     public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 3;
+    public static final int CHANGE_NAVIGATION_MODE = 1 << 4;
 
     public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
-            | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
+            | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS | CHANGE_NAVIGATION_MODE;
+
+    private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+    private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
+    private static final String TARGET_OVERLAY_PACKAGE = "android";
 
     private final Context mContext;
     private final DisplayManager mDM;
 
     // Null for SDK < S
     private final Context mWindowContext;
+
     // The callback in this listener updates DeviceProfile, which other listeners might depend on
     private DisplayInfoChangeListener mPriorityListener;
     private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
 
+    private final SimpleBroadcastReceiver mReceiver = new SimpleBroadcastReceiver(this::onIntent);
+
     private Info mInfo;
     private boolean mDestroyed = false;
 
@@ -95,29 +109,23 @@
             mWindowContext.registerComponentCallbacks(this);
         } else {
             mWindowContext = null;
-            SimpleBroadcastReceiver configChangeReceiver =
-                    new SimpleBroadcastReceiver(this::onConfigChanged);
-            mContext.registerReceiver(configChangeReceiver,
-                    new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+            mReceiver.register(mContext, ACTION_CONFIGURATION_CHANGED);
         }
+
+        // Initialize navigation mode change listener
+        mContext.registerReceiver(mReceiver,
+                getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
+
+        WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
         mInfo = new Info(getDisplayInfoContext(display), display,
-                getInternalDisplays(mDM), emptyMap());
-        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+                wmProxy, wmProxy.estimateInternalDisplayBounds(context));
     }
 
-    private static ArrayMap<String, PortraitSize> getInternalDisplays(
-            DisplayManager displayManager) {
-        Display[] displays = displayManager.getDisplays();
-        ArrayMap<String, PortraitSize> internalDisplays = new ArrayMap<>();
-        for (Display display : displays) {
-            if (ApiWrapper.isInternalDisplay(display)) {
-                Point size = new Point();
-                display.getRealSize(size);
-                internalDisplays.put(ApiWrapper.getUniqueId(display),
-                        new PortraitSize(size.x, size.y));
-            }
-        }
-        return internalDisplays;
+    /**
+     * Returns the current navigation mode
+     */
+    public static NavigationMode getNavigationMode(Context context) {
+        return INSTANCE.get(context).getInfo().navigationMode;
     }
 
     @Override
@@ -128,35 +136,6 @@
         } else {
             // TODO: unregister broadcast receiver
         }
-        mDM.unregisterDisplayListener(this);
-    }
-
-    @Override
-    public final void onDisplayAdded(int displayId) { }
-
-    @Override
-    public final void onDisplayRemoved(int displayId) { }
-
-    @WorkerThread
-    @Override
-    public final void onDisplayChanged(int displayId) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
-        Display display = mDM.getDisplay(DEFAULT_DISPLAY);
-        if (display == null) {
-            return;
-        }
-        if (Utilities.ATLEAST_S) {
-            // Only update refresh rate. Everything else comes from component callbacks
-            mInfo.mSingleFrameMs = getSingleFrameMs(display);
-            return;
-        }
-        handleInfoChange(display);
-    }
-
-    public static int getSingleFrameMs(Context context) {
-        return INSTANCE.get(context).getInfo().mSingleFrameMs;
     }
 
     /**
@@ -173,15 +152,20 @@
         void onDisplayInfoChanged(Context context, Info info, int flags);
     }
 
-    /**
-     * Only used for pre-S
-     */
-    private void onConfigChanged(Intent intent) {
+    private void onIntent(Intent intent) {
         if (mDestroyed) {
             return;
         }
-        Configuration config = mContext.getResources().getConfiguration();
-        if (mInfo.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
+        boolean reconfigure = false;
+        if (ACTION_OVERLAY_CHANGED.equals(intent.getAction())) {
+            reconfigure = true;
+        } else if (ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
+            Configuration config = mContext.getResources().getConfiguration();
+            reconfigure = mInfo.fontScale != config.fontScale
+                    || mInfo.densityDpi != config.densityDpi;
+        }
+
+        if (reconfigure) {
             Log.d(TAG, "Configuration changed, notifying listeners");
             Display display = mDM.getDisplay(DEFAULT_DISPLAY);
             if (display != null) {
@@ -229,15 +213,17 @@
 
     @AnyThread
     private void handleInfoChange(Display display) {
+        WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
         Info oldInfo = mInfo;
 
         Context displayContext = getDisplayInfoContext(display);
-        Info newInfo = new Info(displayContext, display,
-                oldInfo.mInternalDisplays, oldInfo.mPerDisplayBounds);
+        Info newInfo = new Info(displayContext, display, wmProxy, oldInfo.mPerDisplayBounds);
 
-        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
+        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
+                || newInfo.navigationMode != oldInfo.navigationMode) {
             // Cache may not be valid anymore, recreate without cache
-            newInfo = new Info(displayContext, display, getInternalDisplays(mDM), emptyMap());
+            newInfo = new Info(displayContext, display, wmProxy,
+                    wmProxy.estimateInternalDisplayBounds(displayContext));
         }
 
         int change = 0;
@@ -250,12 +236,14 @@
         if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
             change |= CHANGE_DENSITY;
         }
+        if (newInfo.navigationMode != oldInfo.navigationMode) {
+            change |= CHANGE_NAVIGATION_MODE;
+        }
         if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
             change |= CHANGE_SUPPORTED_BOUNDS;
 
-            PortraitSize realSize = new PortraitSize(newInfo.currentSize.x, newInfo.currentSize.y);
-            PortraitSize expectedSize = oldInfo.mInternalDisplays.get(
-                    ApiWrapper.getUniqueId(display));
+            Point currentS = newInfo.currentSize;
+            Point expectedS = oldInfo.mPerDisplayBounds.get(newInfo.displayId).first.size;
             if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
                 Log.e("b/198965093",
                         "Inconsistent number of displays"
@@ -263,7 +251,9 @@
                                 + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
                                 + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
             }
-            if (!realSize.equals(expectedSize) && display.getState() == Display.STATE_OFF) {
+            if ((Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y)
+                    || Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y))
+                    && display.getState() == Display.STATE_OFF) {
                 Log.e("b/198965093", "Display size changed while display is off, ignoring change");
                 return;
             }
@@ -280,100 +270,112 @@
         if (mPriorityListener != null) {
             mPriorityListener.onDisplayInfoChanged(context, mInfo, flags);
         }
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
+
+        int count = mListeners.size();
+        for (int i = 0; i < count; i++) {
             mListeners.get(i).onDisplayInfoChanged(context, mInfo, flags);
         }
     }
 
     public static class Info {
 
-        private int mSingleFrameMs;
-
-        // Configuration properties
+        // Cached property
         public final int rotation;
+        public final String displayId;
+        public final Point currentSize;
+        public final Rect cutout;
+
+        // Configuration property
         public final float fontScale;
         public final int densityDpi;
+        public final NavigationMode navigationMode;
 
         private final PortraitSize mScreenSizeDp;
 
-        public final Point currentSize;
-
-        public String displayId;
         public final Set<WindowBounds> supportedBounds = new ArraySet<>();
-        private final Map<String, Set<WindowBounds>> mPerDisplayBounds = new ArrayMap<>();
-        private final ArrayMap<String, PortraitSize> mInternalDisplays;
+
+        private final ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> mPerDisplayBounds =
+                new ArrayMap<>();
 
         public Info(Context context, Display display) {
-            this(context, display, new ArrayMap<>(), emptyMap());
+            /* don't need system overrides for external displays */
+            this(context, display, new WindowManagerProxy(), new ArrayMap<>());
         }
 
-        private Info(Context context, Display display,
-                ArrayMap<String, PortraitSize> internalDisplays,
-                Map<String, Set<WindowBounds>> perDisplayBoundsCache) {
-            mInternalDisplays = internalDisplays;
-            rotation = display.getRotation();
+        // Used for testing
+        public Info(Context context, Display display,
+                WindowManagerProxy wmProxy,
+                ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache) {
+            CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(display);
+            rotation = displayInfo.rotation;
+            currentSize = displayInfo.size;
+            displayId = displayInfo.id;
+            cutout = displayInfo.cutout;
 
             Configuration config = context.getResources().getConfiguration();
             fontScale = config.fontScale;
             densityDpi = config.densityDpi;
             mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
+            navigationMode = parseNavigationMode(context);
 
-            mSingleFrameMs = getSingleFrameMs(display);
-            currentSize = new Point();
-            display.getRealSize(currentSize);
+            mPerDisplayBounds.putAll(perDisplayBoundsCache);
+            Pair<CachedDisplayInfo, WindowBounds[]> cachedValue = mPerDisplayBounds.get(displayId);
 
-            displayId = ApiWrapper.getUniqueId(display);
-            Set<WindowBounds> currentSupportedBounds =
-                    getSupportedBoundsForDisplay(display, currentSize);
-            mPerDisplayBounds.put(displayId, currentSupportedBounds);
-            supportedBounds.addAll(currentSupportedBounds);
-
-            if (ApiWrapper.isInternalDisplay(display) && internalDisplays.size() > 1) {
-                int displayCount = internalDisplays.size();
-                for (int i = 0; i < displayCount; i++) {
-                    String displayKey = internalDisplays.keyAt(i);
-                    if (TextUtils.equals(displayId, displayKey)) {
-                        continue;
-                    }
-
-                    Set<WindowBounds> displayBounds = perDisplayBoundsCache.get(displayKey);
-                    if (displayBounds == null) {
-                        // We assume densityDpi is the same across all internal displays
-                        displayBounds = WindowManagerCompat.estimateDisplayProfiles(
-                                context, internalDisplays.valueAt(i), densityDpi,
-                                ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
-                    }
-
-                    supportedBounds.addAll(displayBounds);
-                    mPerDisplayBounds.put(displayKey, displayBounds);
+            WindowBounds realBounds = wmProxy.getRealBounds(context, display, displayInfo);
+            if (cachedValue == null) {
+                supportedBounds.add(realBounds);
+            } else {
+                // Verify that the real bounds are a match
+                WindowBounds expectedBounds = cachedValue.second[displayInfo.rotation];
+                if (!realBounds.equals(expectedBounds)) {
+                    WindowBounds[] clone = new WindowBounds[4];
+                    System.arraycopy(cachedValue.second, 0, clone, 0, 4);
+                    clone[displayInfo.rotation] = realBounds;
+                    cachedValue = Pair.create(displayInfo.normalize(), clone);
+                    mPerDisplayBounds.put(displayId, cachedValue);
                 }
             }
+            mPerDisplayBounds.values().forEach(
+                    pair -> Collections.addAll(supportedBounds, pair.second));
             Log.d("b/211775278", "displayId: " + displayId + ", currentSize: " + currentSize);
             Log.d("b/211775278", "perDisplayBounds: " + mPerDisplayBounds);
         }
 
-        private static Set<WindowBounds> getSupportedBoundsForDisplay(Display display, Point size) {
-            Point smallestSize = new Point();
-            Point largestSize = new Point();
-            display.getCurrentSizeRange(smallestSize, largestSize);
-
-            int portraitWidth = Math.min(size.x, size.y);
-            int portraitHeight = Math.max(size.x, size.y);
-            Set<WindowBounds> result = new ArraySet<>();
-            result.add(new WindowBounds(portraitWidth, portraitHeight,
-                    smallestSize.x, largestSize.y));
-            result.add(new WindowBounds(portraitHeight, portraitWidth,
-                    largestSize.x, smallestSize.y));
-            return result;
+        /**
+         * Returns {@code true} if the bounds represent a tablet.
+         */
+        public boolean isTablet(WindowBounds bounds) {
+            return smallestSizeDp(bounds) >= MIN_TABLET_WIDTH;
         }
 
         /**
-         * Returns true if the bounds represent a tablet
+         * Returns {@code true} if the bounds represent a large tablet.
          */
-        public boolean isTablet(WindowBounds bounds) {
-            return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
-                    densityDpi) >= MIN_TABLET_WIDTH;
+        public boolean isLargeTablet(WindowBounds bounds) {
+            return smallestSizeDp(bounds) >= MIN_LARGE_TABLET_WIDTH;
         }
+
+        /**
+         * Returns smallest size in dp for given bounds.
+         */
+        public float smallestSizeDp(WindowBounds bounds) {
+            return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()), densityDpi);
+        }
+    }
+
+    /**
+     * Dumps the current state information
+     */
+    public void dump(PrintWriter pw) {
+        Info info = mInfo;
+        pw.println("DisplayController.Info:");
+        pw.println("  id=" + info.displayId);
+        pw.println("  rotation=" + info.rotation);
+        pw.println("  fontScale=" + info.fontScale);
+        pw.println("  densityDpi=" + info.displayId);
+        pw.println("  navigationMode=" + info.navigationMode.name());
+        pw.println("  currentSize=" + info.currentSize);
+        pw.println("  supportedBounds=" + info.supportedBounds);
     }
 
     /**
@@ -401,8 +403,35 @@
         }
     }
 
-    private static int getSingleFrameMs(Display display) {
-        float refreshRate = display.getRefreshRate();
-        return refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+    public enum NavigationMode {
+        THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
+        TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
+        NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
+
+        public final boolean hasGestures;
+        public final int resValue;
+        public final LauncherEvent launcherEvent;
+
+        NavigationMode(boolean hasGestures, int resValue, LauncherEvent launcherEvent) {
+            this.hasGestures = hasGestures;
+            this.resValue = resValue;
+            this.launcherEvent = launcherEvent;
+        }
+    }
+
+    private static NavigationMode parseNavigationMode(Context context) {
+        int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
+                context.getResources(), INVALID_RESOURCE_HANDLE);
+
+        if (modeInt == INVALID_RESOURCE_HANDLE) {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+        } else {
+            for (NavigationMode m : NavigationMode.values()) {
+                if (m.resValue == modeInt) {
+                    return m;
+                }
+            }
+        }
+        return Utilities.ATLEAST_S ? NavigationMode.NO_BUTTON : NavigationMode.THREE_BUTTONS;
     }
 }
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 6329540..8485371 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -15,10 +15,17 @@
  */
 package com.android.launcher3.util;
 
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
 
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -34,6 +41,9 @@
             Math.max(Runtime.getRuntime().availableProcessors(), 2);
     private static final int KEEP_ALIVE = 1;
 
+    /** Dedicated executor instances for work depending on other packages. */
+    private static final Map<String, ExecutorService> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
+
     /**
      * An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
      */
@@ -76,6 +86,18 @@
             new LooperExecutor(createAndStartNewLooper("launcher-loader"));
 
     /**
+     * Returns and caches a single thread executor for a given package.
+     *
+     * @param packageName Package associated with the executor.
+     */
+    public static ExecutorService getPackageExecutor(String packageName) {
+        return PACKAGE_EXECUTORS.computeIfAbsent(
+                packageName,
+                p -> newSingleThreadExecutor(
+                        new SimpleThreadFactory(p, THREAD_PRIORITY_BACKGROUND)));
+    }
+
+    /**
      * A simple ThreadFactory to set the thread name and priority when used with executors.
      */
     public static class SimpleThreadFactory implements ThreadFactory {
diff --git a/src/com/android/launcher3/util/RotationUtils.java b/src/com/android/launcher3/util/RotationUtils.java
new file mode 100644
index 0000000..3414a3d
--- /dev/null
+++ b/src/com/android/launcher3/util/RotationUtils.java
@@ -0,0 +1,75 @@
+/*
+ * 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.util;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+
+/**
+ * Utility methods based on {@code frameworks/base/core/java/android/util/RotationUtils.java}
+ */
+public class RotationUtils {
+
+    /**
+     * Rotates an Rect according to the given rotation.
+     */
+    public static void rotateRect(Rect rect, int rotation) {
+        switch (rotation) {
+            case ROTATION_0:
+                return;
+            case ROTATION_90:
+                rect.set(rect.top, rect.right, rect.bottom, rect.left);
+                return;
+            case ROTATION_180:
+                rect.set(rect.right, rect.bottom, rect.left, rect.top);
+                return;
+            case ROTATION_270:
+                rect.set(rect.bottom, rect.left, rect.top, rect.right);
+                return;
+            default:
+                throw new IllegalArgumentException("unknown rotation: " + rotation);
+        }
+    }
+
+    /**
+     * Rotates an size according to the given rotation.
+     */
+    public static void rotateSize(Point size, int rotation) {
+        switch (rotation) {
+            case ROTATION_0:
+            case ROTATION_180:
+                return;
+            case ROTATION_90:
+            case ROTATION_270:
+                size.set(size.y, size.x);
+                return;
+            default:
+                throw new IllegalArgumentException("unknown rotation: " + rotation);
+        }
+    }
+
+    /** @return the rotation needed to rotate from oldRotation to newRotation. */
+    public static int deltaRotation(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index cb714b2..b40493a 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -113,6 +113,14 @@
          * the bounds were originally in
          */
         public final boolean appsStackedVertically;
+        /**
+         * If {@code true}, that means at the time of creation of this object, the phone was in
+         * seascape orientation. This is important on devices with insets, because they do not split
+         * evenly -- one of the insets must be slightly larger to account for the inset.
+         * From landscape, it is the leftTop task that expands slightly.
+         * From seascape, it is the rightBottom task that expands slightly.
+         */
+        public final boolean initiatedFromSeascape;
         public final int leftTopTaskId;
         public final int rightBottomTaskId;
 
@@ -128,11 +136,22 @@
                 this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
                         leftTopBounds.right, rightBottomBounds.top);
                 appsStackedVertically = true;
+                initiatedFromSeascape = false;
             } else {
                 // horizontal apps, vertical divider
                 this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
                         rightBottomBounds.left, leftTopBounds.bottom);
                 appsStackedVertically = false;
+                // The following check is unreliable on devices without insets
+                // (initiatedFromSeascape will always be set to false.) This happens to be OK for
+                // all our current uses, but should be refactored.
+                // TODO: Create a more reliable check, or refactor how splitting works on devices
+                //  with insets.
+                if (rightBottomBounds.width() > leftTopBounds.width()) {
+                    initiatedFromSeascape = true;
+                } else {
+                    initiatedFromSeascape = false;
+                }
             }
 
             leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index c92770e..a15679a 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -33,19 +33,27 @@
     public final Rect bounds;
     public final Rect insets;
     public final Point availableSize;
+    public final int rotationHint;
 
     public WindowBounds(Rect bounds, Rect insets) {
+        this(bounds, insets, -1);
+    }
+
+    public WindowBounds(Rect bounds, Rect insets, int rotationHint) {
         this.bounds = bounds;
         this.insets = insets;
+        this.rotationHint = rotationHint;
         availableSize = new Point(bounds.width() - insets.left - insets.right,
                 bounds.height() - insets.top - insets.bottom);
     }
 
-    public WindowBounds(int width, int height, int availableWidth, int availableHeight) {
+    public WindowBounds(int width, int height, int availableWidth, int availableHeight,
+            int rotationHint) {
         this.bounds = new Rect(0, 0, width, height);
         this.availableSize = new Point(availableWidth, availableHeight);
         // We don't care about insets in this case
         this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight);
+        this.rotationHint = rotationHint;
     }
 
     @Override
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
deleted file mode 100644
index e1b9478..0000000
--- a/src/com/android/launcher3/util/WindowManagerCompat.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
-import static com.android.launcher3.Utilities.dpiFromPx;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.ArraySet;
-import android.view.WindowInsets;
-import android.view.WindowInsets.Type;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DisplayController.PortraitSize;
-
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Utility class to estimate window manager values
- */
-@TargetApi(Build.VERSION_CODES.S)
-public class WindowManagerCompat {
-
-    public static final int MIN_TABLET_WIDTH = 600;
-
-    /**
-     * Returns a set of supported render sizes for a internal display.
-     * This is a temporary workaround which assumes only nav-bar insets change across displays, and
-     * is only used until we eventually get the real values
-     * @param consumeTaskBar if true, it assumes that task bar is part of the app window
-     *                       and ignores any insets because of task bar.
-     */
-    public static Set<WindowBounds> estimateDisplayProfiles(
-            Context windowContext, PortraitSize size, int densityDpi, boolean consumeTaskBar) {
-        if (!Utilities.ATLEAST_S) {
-            return Collections.emptySet();
-        }
-        WindowInsets defaultInsets = windowContext.getSystemService(WindowManager.class)
-                .getMaximumWindowMetrics().getWindowInsets();
-        boolean isGesturalMode = ResourceUtils.getIntegerByName(
-                "config_navBarInteractionMode",
-                windowContext.getResources(),
-                INVALID_RESOURCE_HANDLE) == 2;
-
-        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(defaultInsets);
-        Set<WindowBounds> result = new ArraySet<>();
-        int swDP = (int) dpiFromPx(size.width, densityDpi);
-        boolean isTablet = swDP >= MIN_TABLET_WIDTH;
-
-        final Insets portraitNav, landscapeNav;
-        if (isTablet && !consumeTaskBar) {
-            portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
-                    .getDimensionPixelSize(R.dimen.taskbar_size));
-        } else if (!isGesturalMode) {
-            portraitNav = Insets.of(0, 0, 0,
-                    getSystemResource(windowContext, "navigation_bar_height", swDP));
-            landscapeNav = isTablet
-                    ? Insets.of(0, 0, 0, getSystemResource(windowContext,
-                            "navigation_bar_height_landscape", swDP))
-                    : Insets.of(0, 0, getSystemResource(windowContext,
-                            "navigation_bar_width", swDP), 0);
-        } else {
-            portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
-        }
-
-        result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
-                new Rect(0, 0, size.width, size.height),
-                insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build())));
-        result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
-                new Rect(0, 0, size.height, size.width),
-                insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build())));
-        return result;
-    }
-
-    private static int getSystemResource(Context context, String key, int swDp) {
-        int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
-        if (resourceId > 0) {
-            Configuration conf = new Configuration();
-            conf.smallestScreenWidthDp = swDp;
-            return context.createConfigurationContext(conf)
-                    .getResources().getDimensionPixelSize(resourceId);
-        }
-        return 0;
-    }
-}
diff --git a/src/com/android/launcher3/util/window/CachedDisplayInfo.java b/src/com/android/launcher3/util/window/CachedDisplayInfo.java
new file mode 100644
index 0000000..06b9829
--- /dev/null
+++ b/src/com/android/launcher3/util/window/CachedDisplayInfo.java
@@ -0,0 +1,92 @@
+/*
+ * 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.util.window;
+
+import static com.android.launcher3.util.RotationUtils.deltaRotation;
+import static com.android.launcher3.util.RotationUtils.rotateRect;
+import static com.android.launcher3.util.RotationUtils.rotateSize;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Surface;
+
+import java.util.Objects;
+
+/**
+ * Properties on a display
+ */
+public class CachedDisplayInfo {
+
+    public final String id;
+    public final Point size;
+    public final int rotation;
+    public final Rect cutout;
+
+    public CachedDisplayInfo() {
+        this(new Point(0, 0), 0);
+    }
+
+    public CachedDisplayInfo(Point size, int rotation) {
+        this("", size, rotation, new Rect());
+    }
+
+    public CachedDisplayInfo(String id, Point size, int rotation, Rect cutout) {
+        this.id = id;
+        this.size = size;
+        this.rotation = rotation;
+        this.cutout = cutout;
+    }
+
+    /**
+     * Returns a CachedDisplayInfo where the properties are normalized to {@link Surface#ROTATION_0}
+     */
+    public CachedDisplayInfo normalize() {
+        if (rotation == Surface.ROTATION_0) {
+            return this;
+        }
+        Point newSize = new Point(size);
+        rotateSize(newSize, deltaRotation(rotation, Surface.ROTATION_0));
+
+        Rect newCutout = new Rect(cutout);
+        rotateRect(newCutout, deltaRotation(rotation, Surface.ROTATION_0));
+        return new CachedDisplayInfo(id, newSize, Surface.ROTATION_0, newCutout);
+    }
+
+    @Override
+    public String toString() {
+        return "CachedDisplayInfo{"
+                + "id='" + id + '\''
+                + ", size=" + size
+                + ", rotation=" + rotation
+                + ", cutout=" + cutout
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CachedDisplayInfo)) return false;
+        CachedDisplayInfo that = (CachedDisplayInfo) o;
+        return rotation == that.rotation && Objects.equals(id, that.id)
+                && Objects.equals(size, that.size) && Objects.equals(cutout,
+                that.cutout);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, size, rotation, cutout);
+    }
+}
diff --git a/src/com/android/launcher3/util/window/RefreshRateTracker.java b/src/com/android/launcher3/util/window/RefreshRateTracker.java
new file mode 100644
index 0000000..7814617
--- /dev/null
+++ b/src/com/android/launcher3/util/window/RefreshRateTracker.java
@@ -0,0 +1,83 @@
+/*
+ * 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.util.window;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.view.Display;
+
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
+
+/**
+ * Utility class to track refresh rate of the current device
+ */
+public class RefreshRateTracker implements DisplayListener, SafeCloseable {
+
+    private static final MainThreadInitializedObject<RefreshRateTracker> INSTANCE =
+            new MainThreadInitializedObject<>(RefreshRateTracker::new);
+
+    private int mSingleFrameMs = 1;
+
+    private final DisplayManager mDM;
+
+    private RefreshRateTracker(Context context) {
+        mDM = context.getSystemService(DisplayManager.class);
+        updateSingleFrameMs();
+        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+    }
+
+    /**
+     * Returns the single frame time in ms
+     */
+    public static int getSingleFrameMs(Context context) {
+        return INSTANCE.get(context).mSingleFrameMs;
+    }
+
+    @Override
+    public final void onDisplayAdded(int displayId) { }
+
+    @Override
+    public final void onDisplayRemoved(int displayId) { }
+
+    @WorkerThread
+    @Override
+    public final void onDisplayChanged(int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            updateSingleFrameMs();
+        }
+    }
+
+    private void updateSingleFrameMs() {
+        Display display = mDM.getDisplay(DEFAULT_DISPLAY);
+        if (display != null) {
+            float refreshRate = display.getRefreshRate();
+            mSingleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+        }
+    }
+
+    @Override
+    public void close() {
+        mDM.unregisterDisplayListener(this);
+    }
+}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
new file mode 100644
index 0000000..ba3d981
--- /dev/null
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -0,0 +1,311 @@
+/*
+ * 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.util.window;
+
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
+import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
+import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.ResourceUtils.getDimenByName;
+import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static com.android.launcher3.util.RotationUtils.deltaRotation;
+import static com.android.launcher3.util.RotationUtils.rotateRect;
+import static com.android.launcher3.util.RotationUtils.rotateSize;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.Surface;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.WindowBounds;
+
+/**
+ * Utility class for mocking some window manager behaviours
+ */
+public class WindowManagerProxy implements ResourceBasedOverride {
+
+    public static final int MIN_TABLET_WIDTH = 600;
+    public static final int MIN_LARGE_TABLET_WIDTH = 720;
+
+    public static final MainThreadInitializedObject<WindowManagerProxy> INSTANCE =
+            forOverride(WindowManagerProxy.class, R.string.window_manager_proxy_class);
+
+    protected final boolean mTaskbarDrawnInProcess;
+
+    /**
+     * Creates a new instance of proxy, applying any overrides
+     */
+    public static WindowManagerProxy newInstance(Context context) {
+        return Overrides.getObject(WindowManagerProxy.class, context,
+                R.string.window_manager_proxy_class);
+    }
+
+    public WindowManagerProxy() {
+        this(false);
+    }
+
+    protected WindowManagerProxy(boolean taskbarDrawnInProcess) {
+        mTaskbarDrawnInProcess = taskbarDrawnInProcess;
+    }
+
+    /**
+     * Returns a map of normalized info of internal displays to estimated window bounds
+     * for that display
+     */
+    public ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> estimateInternalDisplayBounds(
+            Context context) {
+        Display[] displays = context.getSystemService(DisplayManager.class).getDisplays();
+        ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> result = new ArrayMap<>();
+        for (Display display : displays) {
+            if (isInternalDisplay(display)) {
+                CachedDisplayInfo info = getDisplayInfo(display).normalize();
+                WindowBounds[] bounds = estimateWindowBounds(context, info);
+                result.put(info.id, Pair.create(info, bounds));
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns the real bounds for the provided display after applying any insets normalization
+     */
+    @TargetApi(Build.VERSION_CODES.R)
+    public WindowBounds getRealBounds(Context windowContext,
+            Display display, CachedDisplayInfo info) {
+        if (!Utilities.ATLEAST_R) {
+            Point smallestSize = new Point();
+            Point largestSize = new Point();
+            display.getCurrentSizeRange(smallestSize, largestSize);
+
+            if (info.size.y > info.size.x) {
+                // Portrait
+                return new WindowBounds(info.size.x, info.size.y, smallestSize.x, largestSize.y,
+                        info.rotation);
+            } else {
+                // Landscape
+                new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y,
+                        info.rotation);
+            }
+        }
+
+        WindowMetrics wm = windowContext.getSystemService(WindowManager.class)
+                .getCurrentWindowMetrics();
+
+        Rect insets = new Rect();
+        normalizeWindowInsets(windowContext, wm.getWindowInsets(), insets);
+        return new WindowBounds(wm.getBounds(), insets, info.rotation);
+    }
+
+    /**
+     * Returns an updated insets, accounting for various Launcher UI specific overrides like taskbar
+     */
+    @TargetApi(Build.VERSION_CODES.R)
+    public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
+            Rect outInsets) {
+        if (!Utilities.ATLEAST_R || !mTaskbarDrawnInProcess) {
+            outInsets.set(oldInsets.getSystemWindowInsetLeft(), oldInsets.getSystemWindowInsetTop(),
+                    oldInsets.getSystemWindowInsetRight(), oldInsets.getSystemWindowInsetBottom());
+            return oldInsets;
+        }
+
+        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(oldInsets);
+        Insets navInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+
+        Resources systemRes = context.getResources();
+        Configuration config = systemRes.getConfiguration();
+
+        boolean isTablet = config.smallestScreenWidthDp > MIN_TABLET_WIDTH;
+        boolean isGesture = isGestureNav(context);
+
+        int bottomNav = isTablet
+                ? 0
+                : (config.screenHeightDp > config.screenWidthDp
+                        ? getDimenByName(NAVBAR_HEIGHT, systemRes, 0)
+                        : (isGesture
+                                ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0)
+                                : 0));
+        Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
+        insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+        insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
+
+        // Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
+        // would count towards it). This is used for the bottom protection in All Apps for example.
+        if (isGesture) {
+            Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+            Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+                    oldTappableInsets.right, 0);
+            insetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+        }
+
+        WindowInsets result = insetsBuilder.build();
+        Insets systemWindowInsets = result.getInsetsIgnoringVisibility(
+                WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+        outInsets.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
+                systemWindowInsets.bottom);
+        return result;
+    }
+
+    /**
+     * Returns true if the display is an internal displays
+     */
+    protected boolean isInternalDisplay(Display display) {
+        return display.getDisplayId() == Display.DEFAULT_DISPLAY;
+    }
+
+    /**
+     * Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
+     */
+    public WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo display) {
+        int densityDpi = context.getResources().getConfiguration().densityDpi;
+        int rotation = display.rotation;
+        Rect safeCutout = display.cutout;
+
+        int minSize = Math.min(display.size.x, display.size.y);
+        int swDp = (int) dpiFromPx(minSize, densityDpi);
+
+        Resources systemRes;
+        {
+            Configuration conf = new Configuration();
+            conf.smallestScreenWidthDp = swDp;
+            systemRes = context.createConfigurationContext(conf).getResources();
+        }
+
+        boolean isTablet = swDp >= MIN_TABLET_WIDTH;
+        boolean isTabletOrGesture = isTablet
+                || (Utilities.ATLEAST_R && isGestureNav(context));
+
+        int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0);
+
+        int navBarHeightPortrait, navBarHeightLandscape, navbarWidthLandscape;
+
+        navBarHeightPortrait = isTablet
+                ? (mTaskbarDrawnInProcess
+                        ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
+                : getDimenByName(NAVBAR_HEIGHT, systemRes, 0);
+
+        navBarHeightLandscape = isTablet
+                ? (mTaskbarDrawnInProcess
+                        ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
+                : (isTabletOrGesture
+                        ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) : 0);
+        navbarWidthLandscape = isTabletOrGesture
+                ? 0
+                : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes, 0);
+
+        WindowBounds[] result = new WindowBounds[4];
+        Point tempSize = new Point();
+        for (int i = 0; i < 4; i++) {
+            int rotationChange = deltaRotation(rotation, i);
+            tempSize.set(display.size.x, display.size.y);
+            rotateSize(tempSize, rotationChange);
+            Rect bounds = new Rect(0, 0, tempSize.x, tempSize.y);
+
+            int navBarHeight, navbarWidth;
+            if (tempSize.y > tempSize.x) {
+                navBarHeight = navBarHeightPortrait;
+                navbarWidth = 0;
+            } else {
+                navBarHeight = navBarHeightLandscape;
+                navbarWidth = navbarWidthLandscape;
+            }
+
+            Rect insets = new Rect(safeCutout);
+            rotateRect(insets, rotationChange);
+            insets.top = Math.max(insets.top, statusBarHeight);
+            insets.bottom = Math.max(insets.bottom, navBarHeight);
+
+            if (i == Surface.ROTATION_270 || i == Surface.ROTATION_180) {
+                // On reverse landscape (and in rare-case when the natural orientation of the
+                // device is landscape), navigation bar is on the right.
+                insets.left = Math.max(insets.left, navbarWidth);
+            } else {
+                insets.right = Math.max(insets.right, navbarWidth);
+            }
+            result[i] = new WindowBounds(bounds, insets, i);
+        }
+        return result;
+    }
+
+    protected boolean isGestureNav(Context context) {
+        return ResourceUtils.getIntegerByName("config_navBarInteractionMode",
+                context.getResources(), INVALID_RESOURCE_HANDLE) == 2;
+    }
+
+    /**
+     * Returns a CachedDisplayInfo initialized for the current display
+     */
+    @TargetApi(Build.VERSION_CODES.S)
+    public CachedDisplayInfo getDisplayInfo(Display display) {
+        int rotation = display.getRotation();
+
+        Point size = new Point();
+        display.getRealSize(size);
+
+        Rect cutoutRect = new Rect();
+        if (Utilities.ATLEAST_S) {
+            DisplayCutout cutout = display.getCutout();
+            if (cutout != null) {
+                cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+                        cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
+            }
+        }
+
+        return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutoutRect);
+    }
+
+    /**
+     * Returns a unique ID representing the display
+     */
+    protected String getDisplayId(Display display) {
+        return Integer.toString(display.getDisplayId());
+    }
+
+    /**
+     * Returns rotation of the display associated with the context.
+     */
+    public int getRotation(Context context) {
+        Display d = null;
+        if (Utilities.ATLEAST_R) {
+            try {
+                d = context.getDisplay();
+            } catch (UnsupportedOperationException e) {
+                // Ignore
+            }
+        }
+        return d == null ? DisplayController.INSTANCE.get(context).getInfo().rotation
+                : d.getRotation();
+    }
+}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 3c90eea..93078e4 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -25,7 +25,8 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.allapps.BaseAllAppsContainerView;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.folder.FolderIcon;
@@ -99,7 +100,7 @@
     /**
      * The all apps container, if it exists in this context.
      */
-    default BaseAllAppsContainerView<?> getAppsView() {
+    default ActivityAllAppsContainerView<?> getAppsView() {
         return null;
     }
 
@@ -190,4 +191,14 @@
     default StringCache getStringCache() {
         return null;
     }
+
+    /**
+     * Creates and returns {@link SearchAdapterProvider} for build variant specific search result
+     * views.
+     */
+    @Nullable
+    default SearchAdapterProvider<?> createSearchAdapterProvider(
+            ActivityAllAppsContainerView<?> appsView) {
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/views/AppLauncher.java b/src/com/android/launcher3/views/AppLauncher.java
new file mode 100644
index 0000000..19e66ab
--- /dev/null
+++ b/src/com/android/launcher3/views/AppLauncher.java
@@ -0,0 +1,213 @@
+/*
+ * 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 static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.RunnableList;
+
+/** An {@link ActivityContext} that can also launch app activities and shortcuts safely. */
+public interface AppLauncher extends ActivityContext {
+
+    String TAG = "AppLauncher";
+
+    /**
+     * Safely starts an activity.
+     *
+     * @param v View starting the activity.
+     * @param intent Base intent being launched.
+     * @param item Item associated with the view.
+     * @return {@code true} if the activity starts successfully.
+     */
+    default boolean startActivitySafely(
+            View v, Intent intent, @Nullable ItemInfo item) {
+
+        Context context = (Context) this;
+        if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
+            Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+            return false;
+        }
+
+        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
+        UserHandle user = item == null ? null : item.user;
+
+        // Prepare intent
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (v != null) {
+            intent.setSourceBounds(Utilities.getViewBounds(v));
+        }
+        try {
+            boolean isShortcut = (item instanceof WorkspaceItemInfo)
+                    && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                    || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                    && !((WorkspaceItemInfo) item).isPromise();
+            if (isShortcut) {
+                // Shortcuts need some special checks due to legacy reasons.
+                startShortcutIntentSafely(intent, optsBundle, item);
+            } else if (user == null || user.equals(Process.myUserHandle())) {
+                // Could be launching some bookkeeping activity
+                context.startActivity(intent, optsBundle);
+            } else {
+                context.getSystemService(LauncherApps.class).startMainActivity(
+                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+            }
+            if (item != null) {
+                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+                logAppLaunch(getStatsLogManager(), item, instanceId);
+            }
+            return true;
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+            Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+        }
+        return false;
+    }
+
+    /** Returns {@code true} if an app launch is blocked due to safe mode. */
+    default boolean isAppBlockedForSafeMode() {
+        return false;
+    }
+
+    /**
+     * Creates and logs a new app launch event.
+     */
+    default void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
+            InstanceId instanceId) {
+        statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
+                .log(LAUNCHER_APP_LAUNCH_TAP);
+    }
+
+    /**
+     * Returns launch options for an Activity.
+     *
+     * @param v View initiating a launch.
+     * @param item Item associated with the view.
+     */
+    default ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+        int left = 0, top = 0;
+        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+        if (v instanceof BubbleTextView) {
+            // Launch from center of icon, not entire view
+            Drawable icon = ((BubbleTextView) v).getIcon();
+            if (icon != null) {
+                Rect bounds = icon.getBounds();
+                left = (width - bounds.width()) / 2;
+                top = v.getPaddingTop();
+                width = bounds.width();
+                height = bounds.height();
+            }
+        }
+        ActivityOptions options =
+                ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+
+        options.setLaunchDisplayId(
+                (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
+                        : Display.DEFAULT_DISPLAY);
+        RunnableList callback = new RunnableList();
+        return new ActivityOptionsWrapper(options, callback);
+    }
+
+    /**
+     * Safely launches an intent for a shortcut.
+     *
+     * @param intent Intent to start.
+     * @param optsBundle Optional launch arguments.
+     * @param info Shortcut information.
+     */
+    default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+        try {
+            StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
+            try {
+                // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
+                // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
+                // is enabled by default on NYC.
+                StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
+                        .penaltyLog().build());
+
+                if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
+                    String packageName = intent.getPackage();
+                    startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+                } else {
+                    // Could be launching some bookkeeping activity
+                    ((Context) this).startActivity(intent, optsBundle);
+                }
+            } finally {
+                StrictMode.setVmPolicy(oldPolicy);
+            }
+        } catch (SecurityException e) {
+            if (!onErrorStartingShortcut(intent, info)) {
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * A wrapper around the platform method with Launcher specific checks.
+     */
+    default void startShortcut(String packageName, String id, Rect sourceBounds,
+            Bundle startActivityOptions, UserHandle user) {
+        if (GO_DISABLE_WIDGETS) {
+            return;
+        }
+        try {
+            ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
+                    sourceBounds, startActivityOptions, user);
+        } catch (SecurityException | IllegalStateException e) {
+            Log.e(TAG, "Failed to start shortcut", e);
+        }
+    }
+
+    /**
+     * Invoked when a shortcut fails to launch.
+     * @param intent Shortcut intent that failed to start.
+     * @param info Shortcut information.
+     * @return {@code true} if the error is handled by this callback.
+     */
+    default boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 76dfb3c..f71aa13 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -20,7 +20,7 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.annotation.TargetApi;
 import android.app.WallpaperManager;
diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java
index 1cb27e1..76c465c 100644
--- a/src/com/android/launcher3/views/BubbleTextHolder.java
+++ b/src/com/android/launcher3/views/BubbleTextHolder.java
@@ -22,7 +22,7 @@
 /**
  * Views that contain {@link BubbleTextView} should implement this interface.
  */
-public interface BubbleTextHolder {
+public interface BubbleTextHolder extends IconLabelDotView {
     BubbleTextView getBubbleText();
 
     /**
@@ -32,4 +32,14 @@
      */
     default void onItemInfoUpdated(ItemInfoWithIcon itemInfo) {
     }
+
+    @Override
+    default void setIconVisible(boolean visible) {
+        getBubbleText().setIconVisible(visible);
+    }
+
+    @Override
+    default void setForceHideDot(boolean hide) {
+        getBubbleText().setForceHideDot(hide);
+    }
 }
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index a66b3f9..d1f90e9 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -147,8 +147,7 @@
      * Update the icon UI to match the provided parameters during an animation frame
      */
     public void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
-            int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp,
-            boolean isVerticalBarLayout) {
+            int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp) {
         MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
 
         float dX = mIsRtl
@@ -169,7 +168,7 @@
         }
 
         update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha, isOpening, scale,
-                minSize, lp, isVerticalBarLayout, dp);
+                minSize, lp, dp);
 
         container.setPivotX(0);
         container.setPivotY(0);
@@ -181,7 +180,7 @@
 
     private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
             int fgIconAlpha, boolean isOpening, float scale, float minSize,
-            MarginLayoutParams parentLp, boolean isVerticalBarLayout, DeviceProfile dp) {
+            MarginLayoutParams parentLp, DeviceProfile dp) {
         float dX = mIsRtl
                 ? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
                 : rect.left - parentLp.getMarginStart();
@@ -193,7 +192,7 @@
         float shapeRevealProgress = boundToRange(mapToRange(max(shapeProgressStart, progress),
                 shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1);
 
-        if (isVerticalBarLayout) {
+        if (dp.isLandscape) {
             mOutline.right = (int) (rect.width() / scale);
         } else {
             mOutline.bottom = (int) (rect.height() / scale);
@@ -218,16 +217,16 @@
                 mRevealAnimator.setCurrentFraction(shapeRevealProgress);
             }
 
-            float drawableScale = (isVerticalBarLayout ? mOutline.width() : mOutline.height())
+            float drawableScale = (dp.isLandscape ? mOutline.width() : mOutline.height())
                     / minSize;
-            setBackgroundDrawableBounds(drawableScale, isVerticalBarLayout);
+            setBackgroundDrawableBounds(drawableScale, dp.isLandscape);
             if (isOpening) {
                 // Center align foreground
                 int height = mFinalDrawableBounds.height();
                 int width = mFinalDrawableBounds.width();
-                int diffY = isVerticalBarLayout ? 0
+                int diffY = dp.isLandscape ? 0
                         : (int) (((height * drawableScale) - height) / 2);
-                int diffX = isVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
+                int diffX = dp.isLandscape ? (int) (((width * drawableScale) - width) / 2)
                         : 0;
                 sTmpRect.set(mFinalDrawableBounds);
                 sTmpRect.offset(diffX, diffY);
@@ -247,11 +246,11 @@
         invalidateOutline();
     }
 
-    private void setBackgroundDrawableBounds(float scale, boolean isVerticalBarLayout) {
+    private void setBackgroundDrawableBounds(float scale, boolean isLandscape) {
         sTmpRect.set(mFinalDrawableBounds);
         Utilities.scaleRectAboutCenter(sTmpRect, scale);
         // Since the drawable is at the top of the view, we need to offset to keep it centered.
-        if (isVerticalBarLayout) {
+        if (isLandscape) {
             sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
         } else {
             sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
@@ -269,7 +268,7 @@
      * Sets the icon for this view as part of initial setup
      */
     public void setIcon(@Nullable Drawable drawable, int iconOffset, MarginLayoutParams lp,
-            boolean isOpening, boolean isVerticalBarLayout, DeviceProfile dp) {
+            boolean isOpening, DeviceProfile dp) {
         mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
         if (mIsAdaptiveIcon) {
             boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
@@ -304,7 +303,7 @@
                 Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
             }
 
-            if (isVerticalBarLayout) {
+            if (dp.isLandscape) {
                 lp.width = (int) Math.max(lp.width, lp.height * dp.aspectRatio);
             } else {
                 lp.height = (int) Math.max(lp.height, lp.width * dp.aspectRatio);
@@ -325,7 +324,7 @@
                 bgDrawableStartScale = scale;
                 mOutline.set(0, 0, lp.width, lp.height);
             }
-            setBackgroundDrawableBounds(bgDrawableStartScale, isVerticalBarLayout);
+            setBackgroundDrawableBounds(bgDrawableStartScale, dp.isLandscape);
             mEndRevealRect.set(0, 0, lp.width, lp.height);
             setOutlineProvider(new ViewOutlineProvider() {
                 @Override
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 0f69530..56a1d37 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -43,6 +43,7 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -82,7 +83,6 @@
     private final Launcher mLauncher;
     private final boolean mIsRtl;
 
-    private boolean mIsVerticalBarLayout = false;
     private boolean mIsOpening;
 
     private IconLoadResult mIconLoadResult;
@@ -150,7 +150,7 @@
             float shapeProgressStart, float cornerRadius, boolean isOpening) {
         setAlpha(alpha);
         mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha,
-                isOpening, this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
+                isOpening, this, mLauncher.getDeviceProfile());
     }
 
     @Override
@@ -320,11 +320,11 @@
     @UiThread
     private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge,
             @Nullable Drawable btvIcon, int iconOffset) {
+        final DeviceProfile dp = mLauncher.getDeviceProfile();
         final InsettableFrameLayout.LayoutParams lp =
                 (InsettableFrameLayout.LayoutParams) getLayoutParams();
         mBadge = badge;
-        mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout,
-                mLauncher.getDeviceProfile());
+        mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, dp);
         if (drawable instanceof AdaptiveIconDrawable) {
             final int originalHeight = lp.height;
             final int originalWidth = lp.width;
@@ -332,7 +332,7 @@
             mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
 
             float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
-            if (mIsVerticalBarLayout) {
+            if (dp.isLandscape) {
                 lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
             } else {
                 lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
@@ -565,7 +565,6 @@
         view.recycle();
 
         // Init properties before getting the drawable.
-        view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
         view.mIsOpening = isOpening;
         view.mOriginalIcon = originalView;
         view.mPositionOut = positionOut;
@@ -587,7 +586,7 @@
         view.matchPositionOf(launcher, originalView, isOpening, positionOut);
 
         // We need to add it to the overlay, but keep it invisible until animation starts..
-        view.setVisibility(INVISIBLE);
+        setIconAndDotVisible(view, false);
         parent.addView(view);
         dragLayer.addView(view.mListenerView);
         view.mListenerView.setListener(view::fastFinish);
@@ -596,16 +595,8 @@
             view.mEndRunnable = null;
 
             if (hideOriginal) {
-                if (isOpening) {
-                    setIconAndDotVisible(originalView, true);
-                    view.finish(dragLayer);
-                } else {
-                    originalView.setVisibility(VISIBLE);
-                    if (originalView instanceof IconLabelDotView) {
-                        setIconAndDotVisible(originalView, true);
-                    }
-                    view.finish(dragLayer);
-                }
+                setIconAndDotVisible(originalView, true);
+                view.finish(dragLayer);
             } else {
                 view.finish(dragLayer);
             }
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index e7cb3b3..19c28b4 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -40,8 +40,8 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.window.RefreshRateTracker;
 
 /**
  * Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes
@@ -97,7 +97,7 @@
 
         // Remove after some time, to avoid flickering
         Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
-                DisplayController.getSingleFrameMs(mLauncher));
+                RefreshRateTracker.getSingleFrameMs(mLauncher));
     }
 
     private void removeViewFromParent() {
@@ -159,7 +159,8 @@
             return;
         }
         View icon = mLauncher.getFirstMatchForAppClose(-1,
-                mContract.componentName.getPackageName(), mContract.user);
+                mContract.componentName.getPackageName(), mContract.user,
+                false /* supportsAllAppsState */);
 
         boolean iconChanged = mIcon != icon;
         if (iconChanged) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 867e488..2a9a8a5 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -229,7 +229,7 @@
         Launcher launcher = Launcher.getLauncher(view.getContext());
         launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
                 .setPackage(launcher.getPackageName())
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
         return true;
     }
 
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 00a0050..b12574f 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -163,9 +163,8 @@
             widthUsed = Math.max(widthUsed, minUsedWidth);
         }
 
-        int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
         measureChildWithMargins(mContent, widthMeasureSpec,
-                widthUsed, heightMeasureSpec, heightUsed);
+                widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding);
         setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
                 MeasureSpec.getSize(heightMeasureSpec));
     }
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index cbec642..470a800 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -70,7 +70,7 @@
     public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
         LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
         return info.toBuilder()
-                .setAttribute(LauncherAppWidgetInfo.getAttribute(sourceContainer))
+                .addItemAttributes(LauncherAppWidgetInfo.getAttribute(sourceContainer))
                 .build();
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index daa8fb7..341cb5c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -31,6 +31,7 @@
 import android.graphics.Rect;
 import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -55,7 +56,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.model.UserManagerState;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.SpringRelativeLayout;
@@ -93,14 +96,16 @@
     private static final String KEY_WIDGETS_EDUCATION_DIALOG_SEEN =
             "launcher.widgets_education_dialog_seen";
 
-    private final Rect mInsets = new Rect();
+    private final UserManagerState mUserManagerState = new UserManagerState();
+
     private final boolean mHasWorkProfile;
     private final SparseArray<AdapterHolder> mAdapters = new SparseArray();
     private final UserHandle mCurrentUser = Process.myUserHandle();
     private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter =
             entry -> mCurrentUser.equals(entry.mPkgItem.user);
     private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
-            mPrimaryWidgetsFilter.negate();
+            entry -> !mCurrentUser.equals(entry.mPkgItem.user)
+                    && !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
     @Nullable private ArrowTipView mLatestEducationalTip;
     private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
             new OnLayoutChangeListener() {
@@ -171,6 +176,9 @@
                 : 0;
         mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
                 R.dimen.widget_cell_horizontal_padding);
+
+        mUserManagerState.init(UserCache.INSTANCE.get(context),
+                context.getSystemService(UserManager.class));
     }
 
     public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -259,10 +267,15 @@
         boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
         adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
 
-        mNoWidgetsView.setText(
-                adapterHolder.mAdapterType == AdapterHolder.SEARCH
-                        ? R.string.no_search_results
-                        : R.string.no_widgets_available);
+        if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) {
+            mNoWidgetsView.setText(R.string.no_search_results);
+        } else if (adapterHolder.mAdapterType == AdapterHolder.WORK
+                && mUserManagerState.isAnyProfileQuietModeEnabled()
+                && mActivityContext.getStringCache() != null) {
+            mNoWidgetsView.setText(mActivityContext.getStringCache().workProfilePausedTitle);
+        } else {
+            mNoWidgetsView.setText(R.string.no_widgets_available);
+        }
         mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
     }
 
diff --git a/src_build_config/com/android/launcher3/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
index 49aadf6..9a81d3f 100644
--- a/src_build_config/com/android/launcher3/BuildConfig.java
+++ b/src_build_config/com/android/launcher3/BuildConfig.java
@@ -17,6 +17,11 @@
 package com.android.launcher3;
 
 public final class BuildConfig {
-  public static final String APPLICATION_ID = "com.android.launcher3";
-  public static final boolean DEBUG = false;
+    public static final String APPLICATION_ID = "com.android.launcher3";
+    public static final boolean DEBUG = false;
+    /**
+     * Flag to state if the QSB is on the first screen and placed on the top,
+     * this can be overwritten in other launchers with a different value, if needed.
+     */
+    public static final boolean QSB_ON_FIRST_SCREEN = true;
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 81e3f98..6715749 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -19,7 +19,6 @@
 import android.app.Person;
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
-import android.view.Display;
 
 import com.android.launcher3.Utilities;
 
@@ -32,20 +31,6 @@
     }
 
     /**
-     * Returns true if the display is an internal displays
-     */
-    public static boolean isInternalDisplay(Display display) {
-        return display.getDisplayId() == Display.DEFAULT_DISPLAY;
-    }
-
-    /**
-     * Returns a unique ID representing the display
-     */
-    public static String getUniqueId(Display display) {
-        return Integer.toString(display.getDisplayId());
-    }
-
-    /**
      * Returns the minimum space that should be left empty at the end of hotseat
      */
     public static int getHotseatEndOffset(Context context) {
diff --git a/tests/Android.bp b/tests/Android.bp
index c2c545b..7542d04 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -67,7 +67,8 @@
         "androidx.test.uiautomator_uiautomator",
         "mockito-target-inline-minus-junit4",
         "launcher_log_protos_lite",
-        "truth-prebuilt"
+        "truth-prebuilt",
+        "platform-test-rules",
     ],
     manifest: "AndroidManifest-common.xml",
     platform_apis: true,
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index e44c172..9cc3aed 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -94,7 +94,6 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
-                <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
 
@@ -138,6 +137,7 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
             <intent-filter>
                 <action android:name="com.android.launcher3.intent.action.test_shortcut"/>
@@ -267,6 +267,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity-alias android:name="Activity15" android:exported="true"
+            android:label="ThemeIconTestActivity"
+            android:icon="@drawable/test_theme_icon"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
 
         <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
         <provider
diff --git a/tests/res/drawable/test_theme_icon.xml b/tests/res/drawable/test_theme_icon.xml
new file mode 100644
index 0000000..be5c4d9
--- /dev/null
+++ b/tests/res/drawable/test_theme_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2021 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@android:color/white"/>
+    <foreground>
+        <color android:color="#FFFF0000" />
+    </foreground>
+    <monochrome>
+        <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+            <path
+                android:fillColor="#FF000000"
+                android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+        </vector>
+    </monochrome>
+</adaptive-icon>
diff --git a/tests/src/com/android/launcher3/DeviceProfileTest.kt b/tests/src/com/android/launcher3/DeviceProfileTest.kt
index 6c99a21..d1e91ed 100644
--- a/tests/src/com/android/launcher3/DeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileTest.kt
@@ -39,6 +39,7 @@
     private var isMultiWindowMode: Boolean = false
     private var transposeLayoutWithOrientation: Boolean = false
     private var useTwoPanels: Boolean = false
+    private var isGestureMode: Boolean = true
 
     @Before
     fun setUp() {
@@ -58,7 +59,8 @@
             windowBounds,
             isMultiWindowMode,
             transposeLayoutWithOrientation,
-            useTwoPanels
+            useTwoPanels,
+            isGestureMode
         )
 
         assertThat(dp.isQsbInline).isFalse()
@@ -67,7 +69,7 @@
 
     @Test
     fun qsbWidth_is_match_parent_for_tablet_portrait() {
-        initializeVarsForTablet()
+        initializeVarsForLargeTablet()
 
         val dp = DeviceProfile(
             context,
@@ -76,7 +78,8 @@
             windowBounds,
             isMultiWindowMode,
             transposeLayoutWithOrientation,
-            useTwoPanels
+            useTwoPanels,
+            isGestureMode
         )
 
         assertThat(dp.isQsbInline).isFalse()
@@ -84,8 +87,8 @@
     }
 
     @Test
-    fun qsbWidth_has_size_for_tablet_landscape() {
-        initializeVarsForTablet(true)
+    fun qsbWidth_has_size_for_large_tablet_landscape() {
+        initializeVarsForLargeTablet(true)
 
         val dp = DeviceProfile(
             context,
@@ -94,7 +97,8 @@
             windowBounds,
             isMultiWindowMode,
             transposeLayoutWithOrientation,
-            useTwoPanels
+            useTwoPanels,
+            isGestureMode
         )
 
         if (dp.hotseatQsbHeight > 0) {
@@ -110,8 +114,8 @@
      * This test is to make sure that two panels don't inline the QSB as tablets do
      */
     @Test
-    fun qsbWidth_is_match_parent_for_two_panel_landscape() {
-        initializeVarsForTablet(true)
+    fun qsbWidth_is_match_parent_for_small_two_panel_landscape() {
+        initializeVarsForSmallTablet(true)
         useTwoPanels = true
 
         val dp = DeviceProfile(
@@ -121,7 +125,8 @@
             windowBounds,
             isMultiWindowMode,
             transposeLayoutWithOrientation,
-            useTwoPanels
+            useTwoPanels,
+            isGestureMode
         )
 
         assertThat(dp.isQsbInline).isFalse()
@@ -134,22 +139,38 @@
         else
             Pair(1440, 3120)
 
-        windowBounds = WindowBounds(x, y, x, y - 100)
+        windowBounds = WindowBounds(x, y, x, y - 100, 0)
 
         `when`(info.isTablet(any())).thenReturn(false)
+        `when`(info.isLargeTablet(any())).thenReturn(false)
 
         scalableInvariantDeviceProfile()
     }
 
-    private fun initializeVarsForTablet(isLandscape: Boolean = false) {
+    private fun initializeVarsForSmallTablet(isLandscape: Boolean = false) {
         val (x, y) = if (isLandscape)
             Pair(2560, 1600)
         else
             Pair(1600, 2560)
 
-        windowBounds = WindowBounds(x, y, x, y - 100)
+        windowBounds = WindowBounds(x, y, x, y - 100, 0)
 
         `when`(info.isTablet(any())).thenReturn(true)
+        `when`(info.isLargeTablet(any())).thenReturn(false)
+
+        scalableInvariantDeviceProfile()
+    }
+
+    private fun initializeVarsForLargeTablet(isLandscape: Boolean = false) {
+        val (x, y) = if (isLandscape)
+            Pair(2560, 1600)
+        else
+            Pair(1600, 2560)
+
+        windowBounds = WindowBounds(x, y, x, y - 100, 0)
+
+        `when`(info.isTablet(any())).thenReturn(true)
+        `when`(info.isLargeTablet(any())).thenReturn(true)
 
         scalableInvariantDeviceProfile()
     }
@@ -187,6 +208,12 @@
                 PointF(64f, 83f),
                 PointF(64f, 83f)
             ).toTypedArray()
+            allAppsCellSize = listOf(
+                PointF(64f, 83f),
+                PointF(64f, 83f),
+                PointF(64f, 83f),
+                PointF(64f, 83f)
+            ).toTypedArray()
         }
     }
 }
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index f34a29e..92e3e64 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -34,6 +34,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 
 /**
@@ -44,6 +45,8 @@
 public class PromiseIconUiTest extends AbstractLauncherUiTest {
 
     private int mSessionId = -1;
+    // TODO(b/202985412): Revert to default timeout when PackageManager bug is fixed.
+    private static final long PROMISE_ICON_TIMEOUT = TimeUnit.SECONDS.toMillis(60);
 
     @Override
     public void setUp() throws Exception {
@@ -85,7 +88,8 @@
 
         // Verify promise icon is added
         waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
-                launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) != null,
+                PROMISE_ICON_TIMEOUT);
 
         // Remove session
         mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
@@ -93,7 +97,8 @@
 
         // Verify promise icon is removed
         waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
-                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null,
+                PROMISE_ICON_TIMEOUT);
     }
 
     @Test
@@ -111,6 +116,7 @@
 
         // Verify promise icon is not added
         waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
-                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null,
+                PROMISE_ICON_TIMEOUT);
     }
 }
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
index 239e092..90d7b43 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
@@ -101,16 +101,21 @@
         modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
         modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
         // Src grid icons
-        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage5, 5, TMP_CONTENT_URI)
-        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage6, 6, TMP_CONTENT_URI)
-        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage8, 8, TMP_CONTENT_URI)
-        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage9, 9, TMP_CONTENT_URI)
-        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage10, 10, TMP_CONTENT_URI)
+        // _ _ _ _ _
+        // _ _ _ _ 5
+        // _ _ 6 _ 7
+        // _ _ 8 _ 9
+        // _ _ _ _ _
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage9, 9, TMP_CONTENT_URI)
 
         // Dest hotseat icons
         modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
         // Dest grid icons
-        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage10)
 
         idp.numDatabaseHotseatIcons = 4
         idp.numColumns = 4
@@ -125,7 +130,7 @@
             idp.numDatabaseHotseatIcons,
             Point(idp.numColumns, idp.numRows)
         )
-        task.migrate(idp)
+        task.migrate(DeviceGridState(context), DeviceGridState(idp))
 
         // Check hotseat items
         var c = context.contentResolver.query(
@@ -174,13 +179,18 @@
                 Point(c.getInt(cellXIndex), c.getInt(cellYIndex))
         }
         c.close()
+        // Expected dest grid icons
+        // _ _ _ _
+        // 5 6 7 8
+        // 9 _ 10_
+        // _ _ _ _
         assertThat(locMap.size.toLong()).isEqualTo(6)
-        assertThat(locMap[testPackage8]).isEqualTo(Point(0, 2))
-        assertThat(locMap[testPackage6]).isEqualTo(Point(0, 3))
-        assertThat(locMap[testPackage10]).isEqualTo(Point(1, 3))
-        assertThat(locMap[testPackage7]).isEqualTo(Point(2, 2))
-        assertThat(locMap[testPackage5]).isEqualTo(Point(2, 3))
-        assertThat(locMap[testPackage9]).isEqualTo(Point(3, 3))
+        assertThat(locMap[testPackage5]).isEqualTo(Point(0, 1))
+        assertThat(locMap[testPackage6]).isEqualTo(Point(1, 1))
+        assertThat(locMap[testPackage7]).isEqualTo(Point(2, 1))
+        assertThat(locMap[testPackage8]).isEqualTo(Point(3, 1))
+        assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2))
+        assertThat(locMap[testPackage10]).isEqualTo(Point(2, 2))
     }
 
     @Test
@@ -205,7 +215,7 @@
             idp.numDatabaseHotseatIcons,
             Point(idp.numColumns, idp.numRows)
         )
-        task.migrate(idp)
+        task.migrate(DeviceGridState(context), DeviceGridState(idp))
 
         // Check hotseat items
         val c = context.contentResolver.query(
@@ -260,7 +270,7 @@
             idp.numDatabaseHotseatIcons,
             Point(idp.numColumns, idp.numRows)
         )
-        task.migrate(idp)
+        task.migrate(DeviceGridState(context), DeviceGridState(idp))
 
         // Check hotseat items
         val c = context.contentResolver.query(
@@ -325,7 +335,7 @@
             idp.numDatabaseHotseatIcons,
             Point(idp.numColumns, idp.numRows)
         )
-        task.migrate(idp)
+        task.migrate(DeviceGridState(context), DeviceGridState(idp))
 
         // Get workspace items
         val c = context.contentResolver.query(
@@ -385,7 +395,7 @@
             idp.numDatabaseHotseatIcons,
             Point(idp.numColumns, idp.numRows)
         )
-        task.migrate(idp)
+        task.migrate(DeviceGridState(context), DeviceGridState(idp))
 
         // Get workspace items
         val c = context.contentResolver.query(
@@ -446,7 +456,7 @@
             idp.numDatabaseHotseatIcons,
             Point(idp.numColumns, idp.numRows)
         )
-        task.migrate(idp)
+        task.migrate(DeviceGridState(context), DeviceGridState(idp))
 
         // Get workspace items
         val c = context.contentResolver.query(
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 79a4673..136f115 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -33,6 +33,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.graphics.Point;
 import android.os.Debug;
 import android.os.Process;
 import android.os.RemoteException;
@@ -54,6 +55,8 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.tapl.HomeAppIcon;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.tapl.TestHelpers;
@@ -564,6 +567,7 @@
                             ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
                     break;
                 }
+                case TASKBAR_ALL_APPS:
                 case LAUNCHED_APP: {
                     assertTrue("Launcher is resumed in state: " + expectedContainerType,
                             !isResumed);
@@ -577,9 +581,10 @@
             }
         } else {
             assertTrue(
-                    "Container type is not LAUNCHED_APP or FALLBACK_OVERVIEW: "
-                            + expectedContainerType,
+                    "Container type is not LAUNCHED_APP, TASKBAR_ALL_APPS "
+                            + "or FALLBACK_OVERVIEW: " + expectedContainerType,
                     expectedContainerType == ContainerType.LAUNCHED_APP
+                            || expectedContainerType == ContainerType.TASKBAR_ALL_APPS
                             || expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
         }
     }
@@ -600,4 +605,26 @@
 
     protected void onLauncherActivityClose(Launcher launcher) {
     }
+
+    protected HomeAppIcon createShortcutInCenterIfNotExist(String name) {
+        Point dimension = mLauncher.getWorkspace().getIconGridDimensions();
+        return createShortcutIfNotExist(name, dimension.x / 2, dimension.y / 2);
+    }
+
+    protected HomeAppIcon createShortcutIfNotExist(String name, int cellX, int cellY) {
+        HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
+        if (homeAppIcon == null) {
+            HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+            allApps.freeze();
+            try {
+                allApps.getAppIcon(name).dragToWorkspace(cellX, cellY);
+            } finally {
+                allApps.unfreeze();
+            }
+            homeAppIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
+        }
+        return homeAppIcon;
+    }
+
+
 }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index da8bf6e..73e9823 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -109,7 +109,7 @@
                 launcher -> assertNotNull("Launcher internal state didn't switch to Showing Menu",
                         launcher.getOptionsPopup()));
         // Check that pressHome works when the menu is shown.
-        mLauncher.pressHome();
+        mLauncher.goHome();
     }
 
     @Test
@@ -121,7 +121,7 @@
         } finally {
             allApps.unfreeze();
         }
-        mLauncher.pressHome();
+        mLauncher.goHome();
     }
 
     public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -273,7 +273,7 @@
         executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
                 getWidgetsScroll(launcher) < flingForwardY));
 
-        mLauncher.pressHome();
+        mLauncher.goHome();
         waitForLauncherCondition("Widgets were not closed",
                 launcher -> getWidgetsView(launcher) == null);
     }
@@ -365,27 +365,11 @@
         }
     }
 
-    private HomeAppIcon createShortcutIfNotExist(String name) {
-        HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
-        if (homeAppIcon == null) {
-            HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
-            allApps.freeze();
-            try {
-                allApps.getAppIcon(name).dragToWorkspace(false, false);
-            } finally {
-                allApps.unfreeze();
-            }
-            homeAppIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
-        }
-        return homeAppIcon;
-    }
-
-    @Ignore("b/205014516")
     @Test
     @PortraitLandscape
     public void testDragToFolder() throws Exception {
-        final HomeAppIcon playStoreIcon = createShortcutIfNotExist("Play Store");
-        final HomeAppIcon gmailIcon = createShortcutIfNotExist("Gmail");
+        final HomeAppIcon playStoreIcon = createShortcutIfNotExist("Play Store", 0, 1);
+        final HomeAppIcon gmailIcon = createShortcutIfNotExist("Gmail", 1, 1);
 
         FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
 
@@ -399,7 +383,7 @@
         assertNull("Play Store should be moved to a folder.",
                 workspace.tryGetWorkspaceAppIcon("Play Store"));
 
-        final HomeAppIcon youTubeIcon = createShortcutIfNotExist("YouTube");
+        final HomeAppIcon youTubeIcon = createShortcutInCenterIfNotExist("YouTube");
 
         folderIcon = youTubeIcon.dragToIcon(folderIcon);
         folder = folderIcon.open();
@@ -433,8 +417,8 @@
     @PortraitLandscape
     public void testDeleteFromWorkspace() throws Exception {
         // test delete both built-in apps and user-installed app from workspace
-        for (String appName : new String[] {"Gmail", "Play Store", APP_NAME}) {
-            final HomeAppIcon homeAppIcon = createShortcutIfNotExist(appName);
+        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));
@@ -458,7 +442,7 @@
         TestUtil.installDummyApp();
         try {
             verifyAppUninstalledFromAllApps(
-                    createShortcutIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
+                    createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
         } finally {
             TestUtil.uninstallDummyApp();
         }
@@ -509,7 +493,7 @@
         }
 
         // test to move a shortcut to other cell.
-        final HomeAppIcon launcherTestAppIcon = createShortcutIfNotExist(APP_NAME);
+        final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(APP_NAME);
         for (Point target : targets) {
             launcherTestAppIcon.dragToWorkspace(target.x, target.y);
         }
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0c1f0cd..9fb52fc 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -169,7 +169,7 @@
         }
 
         // Go back to home
-        mLauncher.pressHome();
+        mLauncher.goHome();
         Wait.atMost("", new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
                 mLauncher);
     }
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
new file mode 100644
index 0000000..e66810c
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ui.workspace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.icons.ThemedIconDrawable;
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+
+import org.junit.Test;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * Tests for theme icon support in Launcher
+ *
+ * Note running these tests will clear the workspace on the device.
+ */
+@LargeTest
+public class ThemeIconsTest extends AbstractLauncherUiTest {
+
+    private static final String APP_NAME = "ThemeIconTestActivity";
+
+    @Test
+    public void testIconWithoutTheme() throws Exception {
+        setThemeEnabled(false);
+        TaplTestsLauncher3.initialize(this);
+
+        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+
+        try {
+            HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
+            executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+            icon.dragToWorkspace(false, false);
+            executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), false));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    @Test
+    public void testIconWithTheme() throws Exception {
+        setThemeEnabled(true);
+        TaplTestsLauncher3.initialize(this);
+
+        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+
+        try {
+            HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
+            executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+            icon.dragToWorkspace(false, false);
+            executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), true));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    private void verifyIconTheme(ViewGroup parent, boolean isThemed) {
+        // Find the app icon
+        Queue<View> viewQueue = new ArrayDeque<>();
+        viewQueue.add(parent);
+        BubbleTextView icon = null;
+        while (!viewQueue.isEmpty()) {
+            View view = viewQueue.poll();
+            if (view instanceof ViewGroup) {
+                parent = (ViewGroup) view;
+                for (int i = parent.getChildCount() - 1; i >= 0; i--) {
+                    viewQueue.add(parent.getChildAt(i));
+                }
+            } else if (view instanceof BubbleTextView) {
+                BubbleTextView btv = (BubbleTextView) view;
+                if (APP_NAME.equals(btv.getText())) {
+                    icon = btv;
+                    break;
+                }
+            }
+        }
+
+        assertNotNull(icon.getIcon());
+        assertEquals(isThemed, icon.getIcon() instanceof ThemedIconDrawable);
+    }
+
+    private void setThemeEnabled(boolean isEnabled) throws Exception {
+        Uri uri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(mTargetPackage + ".grid_control")
+                .appendPath("set_icon_themed")
+                .build();
+        ContentValues values = new ContentValues();
+        values.put("boolean_value", isEnabled);
+        try (ContentProviderClient client = mTargetContext.getContentResolver()
+                .acquireContentProviderClient(uri)) {
+            int result = client.update(uri, values, null);
+            assertTrue(result > 0);
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 59966ee..3324959 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -67,6 +67,7 @@
 import com.android.launcher3.testing.TestInformationProvider;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import org.mockito.ArgumentCaptor;
@@ -501,7 +502,7 @@
                     LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
                     DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
                     SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
-                    ItemInstallQueue.INSTANCE);
+                    ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
             mPm = spy(getBaseContext().getPackageManager());
             mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 3658f41..bfb115d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -74,7 +74,7 @@
             LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height");
             return false;
         }
-        if (iconCenterInSearchBox(allAppsContainer, icon)) {
+        if (hasSearchBox() && iconCenterInSearchBox(allAppsContainer, icon)) {
             LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
             return false;
         }
@@ -107,7 +107,7 @@
             final UiObject2 allAppsContainer = verifyActiveContainer();
             final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
                     "apps_list_view");
-            final UiObject2 searchBox = getSearchBox(allAppsContainer);
+            final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
 
             int deviceHeight = mLauncher.getRealDisplaySize().y;
             int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
@@ -128,8 +128,10 @@
                                                 mLauncher.getVisibleBounds(icon).top
                                                         < bottomGestureStartOnScreen)
                                         .collect(Collectors.toList()),
-                                mLauncher.getVisibleBounds(searchBox).bottom
-                                        - mLauncher.getVisibleBounds(allAppsContainer).top);
+                                hasSearchBox()
+                                        ? mLauncher.getVisibleBounds(searchBox).bottom
+                                        - mLauncher.getVisibleBounds(allAppsContainer).top
+                                        : 0);
                         verifyActiveContainer();
                         final int newScroll = getAllAppsScroll();
                         mLauncher.assertTrue(
@@ -173,18 +175,21 @@
         return appIcon;
     }
 
+    @NonNull
     protected abstract AppIcon createAppIcon(UiObject2 icon);
 
+    protected abstract boolean hasSearchBox();
+
     private void scrollBackToBeginning() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to scroll back in all apps")) {
             LauncherInstrumentation.log("Scrolling to the beginning");
             final UiObject2 allAppsContainer = verifyActiveContainer();
-            final UiObject2 searchBox = getSearchBox(allAppsContainer);
+            final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
 
             int attempts = 0;
-            final Rect margins =
-                    new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5);
+            final Rect margins = new Rect(
+                    0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
 
             for (int scroll = getAllAppsScroll();
                     scroll != 0;
@@ -196,7 +201,11 @@
                         ++attempts <= MAX_SCROLL_ATTEMPTS);
 
                 mLauncher.scroll(
-                        allAppsContainer, Direction.UP, margins, 12, false);
+                        allAppsContainer,
+                        Direction.UP,
+                        margins,
+                        /* steps= */ 12,
+                        /* slowDown= */ false);
             }
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -225,7 +234,11 @@
             final UiObject2 allAppsContainer = verifyActiveContainer();
             // Start the gesture in the center to avoid starting at elements near the top.
             mLauncher.scroll(
-                    allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10, false);
+                    allAppsContainer,
+                    Direction.DOWN,
+                    new Rect(0, 0, 0, mHeight / 2),
+                    /* steps= */ 10,
+                    /* slowDown= */ false);
             verifyActiveContainer();
         }
     }
@@ -240,7 +253,11 @@
             final UiObject2 allAppsContainer = verifyActiveContainer();
             // Start the gesture in the center, for symmetry with forward.
             mLauncher.scroll(
-                    allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10, false);
+                    allAppsContainer,
+                    Direction.UP,
+                    new Rect(0, mHeight / 2, 0, 0),
+                    /* steps= */ 10,
+                    /*slowDown= */ false);
             verifyActiveContainer();
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
new file mode 100644
index 0000000..5164025
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApps opened from the Taskbar.
+ */
+public class AllAppsFromTaskbar extends AllApps {
+
+    AllAppsFromTaskbar(LauncherInstrumentation launcher) {
+        super(launcher);
+    }
+
+    @Override
+    protected LauncherInstrumentation.ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.TASKBAR_ALL_APPS;
+    }
+
+    @NonNull
+    @Override
+    public TaskbarAppIcon getAppIcon(String appName) {
+        return (TaskbarAppIcon) super.getAppIcon(appName);
+    }
+
+    @NonNull
+    @Override
+    protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
+        return new TaskbarAppIcon(mLauncher, icon);
+    }
+
+    @Override
+    protected boolean hasSearchBox() {
+        return false;
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index bef242c..e28f0af 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -51,7 +51,7 @@
     public AppIconMenu openMenu() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             return createMenu(mLauncher.clickAndGet(
-                    mObject, "popup_container", getLongClickEvent()));
+                    mObject, /* resName= */ "popup_container", getLongClickEvent()));
         }
     }
 
@@ -61,7 +61,7 @@
     public AppIconMenu openDeepShortcutMenu() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             return createMenu(mLauncher.clickAndGet(
-                    mObject, "deep_shortcuts_container", getLongClickEvent()));
+                    mObject, /* resName= */ "deep_shortcuts_container", getLongClickEvent()));
         }
     }
 
@@ -73,8 +73,8 @@
     }
 
     @Override
-    protected String getLongPressIndicator() {
-        return "popup_container";
+    protected void waitForLongPressConfirmation() {
+        mLauncher.waitForLauncherObject("popup_container");
     }
 
     @Override
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index a6a1531..5cf5aba 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -41,8 +41,8 @@
     }
 
     @Override
-    protected String getLongPressIndicator() {
-        return "drop_target_bar";
+    protected void waitForLongPressConfirmation() {
+        mLauncher.waitForLauncherObject("drop_target_bar");
     }
 
     @Override
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 2e00d59..c275f3b 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -35,8 +35,14 @@
         return (AllAppsAppIcon) super.getAppIcon(appName);
     }
 
+    @NonNull
     @Override
     protected HomeAppIcon createAppIcon(UiObject2 icon) {
         return new AllAppsAppIcon(mLauncher, icon);
     }
+
+    @Override
+    protected boolean hasSearchBox() {
+        return true;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
index 4b36dc0..71d8ba9 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
@@ -51,8 +51,7 @@
                     () -> {
                         final Rect bounds = target.getDropLocationBounds();
                         return new Point(bounds.centerX(), bounds.centerY());
-                    },
-                    getLongPressIndicator());
+                    });
             FolderIcon result = target.getTargetFolder(dropBounds);
             mLauncher.assertTrue("Can't find the target folder.", result != null);
             return result;
@@ -115,8 +114,12 @@
                      String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))
         ) {
             final Supplier<Point> dest = () -> Workspace.getCellCenter(mLauncher, cellX, cellY);
-            Workspace.dragIconToWorkspace(mLauncher, this, dest, true, getLongPressIndicator(),
-                    () -> addExpectedEventsForLongClick(), null);
+            Workspace.dragIconToWorkspace(
+                    mLauncher,
+                    /* launchable= */ this,
+                    dest,
+                    () -> addExpectedEventsForLongClick(),
+                    /*expectDropEvents= */ null);
             try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) {
                 WorkspaceAppIcon appIcon =
                         (WorkspaceAppIcon) mLauncher.getWorkspace().getWorkspaceAppIcon(mAppName);
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 02a6b91..45a0196 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -97,8 +97,7 @@
         return new LaunchedAppState(mLauncher);
     }
 
-    Point startDrag(long downTime, String longPressIndicator,
-            Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
+    Point startDrag(long downTime, Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
         final Point iconCenter = getObject().getVisibleCenter();
         final Point dragStartCenter = new Point(iconCenter.x,
                 iconCenter.y - getStartDragThreshold());
@@ -108,7 +107,6 @@
                     downTime,
                     iconCenter,
                     dragStartCenter,
-                    longPressIndicator,
                     expectLongClickEvents),
                     SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
         } else {
@@ -116,7 +114,6 @@
                     downTime,
                     iconCenter,
                     dragStartCenter,
-                    longPressIndicator,
                     expectLongClickEvents);
         }
 
@@ -125,21 +122,43 @@
     }
 
     /**
+     * Waits for a confirmation that a long press has successfully been triggered.
+     *
+     * This method waits for a view to either appear or disappear to confirm that the long press
+     * has been triggered and fails if no confirmation is received before the default timeout.
+     */
+    protected abstract void waitForLongPressConfirmation();
+
+    /**
      * Drags this Launchable a short distance before starting a full drag.
      *
      * This is necessary for shortcuts, which require being dragged beyond a threshold to close
      * their container and start drag callbacks.
      */
-    private void movePointerForStartDrag(long downTime, Point iconCenter, Point dragStartCenter,
-            String longPressIndicator, Runnable expectLongClickEvents) {
-        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
-                iconCenter, LauncherInstrumentation.GestureScope.INSIDE);
+    private void movePointerForStartDrag(
+            long downTime,
+            Point iconCenter,
+            Point dragStartCenter,
+            Runnable expectLongClickEvents) {
+        mLauncher.sendPointer(
+                downTime,
+                downTime,
+                MotionEvent.ACTION_DOWN,
+                iconCenter,
+                LauncherInstrumentation.GestureScope.INSIDE);
         LauncherInstrumentation.log("movePointerForStartDrag: sent down");
         expectLongClickEvents.run();
-        mLauncher.waitForLauncherObject(longPressIndicator);
+        waitForLongPressConfirmation();
         LauncherInstrumentation.log("movePointerForStartDrag: indicator");
-        mLauncher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false,
-                downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+        mLauncher.movePointer(
+                iconCenter,
+                dragStartCenter,
+                DEFAULT_DRAG_STEPS,
+                /* isDecelerating= */ false,
+                downTime,
+                downTime,
+                /* slowDown= */ true,
+                LauncherInstrumentation.GestureScope.INSIDE);
     }
 
     private int getStartDragThreshold() {
@@ -148,6 +167,4 @@
     }
 
     protected abstract void addExpectedEventsForLongClick();
-
-    protected abstract String getLongPressIndicator();
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 3f1be80..2033a42 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -16,11 +16,27 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.test.uiautomator.By;
+
+import com.android.launcher3.testing.TestProtocol;
+
 /**
  * Background state operations specific to when an app has been launched.
  */
 public final class LaunchedAppState extends Background {
 
+    // More drag steps than Launchables to give the window manager time to register the drag.
+    private static final int DEFAULT_DRAG_STEPS = 35;
+
     LaunchedAppState(LauncherInstrumentation launcher) {
         super(launcher);
     }
@@ -29,4 +45,126 @@
     protected LauncherInstrumentation.ContainerType getContainerType() {
         return LauncherInstrumentation.ContainerType.LAUNCHED_APP;
     }
+
+    /**
+     * Returns the taskbar.
+     *
+     * The taskbar must already be visible when calling this method.
+     */
+    public Taskbar getTaskbar() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get the taskbar")) {
+            mLauncher.waitForLauncherObject("taskbar_view");
+
+            return new Taskbar(mLauncher);
+        }
+    }
+
+    /**
+     * Returns the Taskbar in a visible state.
+     *
+     * The taskbar must already be hidden when calling this method.
+     */
+    public Taskbar showTaskbar() {
+        mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                     "want to show the taskbar")) {
+            mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+
+            final long downTime = SystemClock.uptimeMillis();
+            final int unstashTargetY = mLauncher.getRealDisplaySize().y
+                    - (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT)
+                            .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2);
+            final Point unstashTarget = new Point(
+                    mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
+
+            mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
+                    LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+            LauncherInstrumentation.log("showTaskbar: sent down");
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+                mLauncher.waitForLauncherObject("taskbar_view");
+                mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
+                        LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+
+                return new Taskbar(mLauncher);
+            }
+        } finally {
+            mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+        }
+    }
+
+    static void dragToSplitscreen(
+            LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
+            String expectedExistingPackageName) {
+        try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
+                "want to drag taskbar item to splitscreen")) {
+            final Point displaySize = launcher.getRealDisplaySize();
+            final Point endPoint = new Point(displaySize.x / 4, 3 * displaySize.y / 4);
+            final long downTime = SystemClock.uptimeMillis();
+            // Use mObject before starting drag since the system drag and drop moves the original
+            // view.
+            Point itemVisibleCenter = launchable.mObject.getVisibleCenter();
+            Rect itemVisibleBounds = launcher.getVisibleBounds(launchable.mObject);
+            String itemLabel = launchable.mObject.getText();
+
+            Point dragStart = launchable.startDrag(
+                    downTime,
+                    launchable::addExpectedEventsForLongClick,
+                    /* runToSpringLoadedState= */ false);
+
+            try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer(
+                    "started item drag")) {
+                launcher.movePointer(
+                        dragStart,
+                        endPoint,
+                        DEFAULT_DRAG_STEPS,
+                        /* isDecelerating= */ true,
+                        downTime,
+                        SystemClock.uptimeMillis(),
+                        /* slowDown= */ false,
+                        LauncherInstrumentation.GestureScope.INSIDE);
+
+                try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer(
+                        "moved pointer to drop point")) {
+                    dropDraggedItem(
+                            launcher,
+                            launchable,
+                            expectedNewPackageName,
+                            endPoint, downTime,
+                            itemVisibleCenter,
+                            itemVisibleBounds,
+                            itemLabel,
+                            expectedExistingPackageName);
+                }
+            }
+        }
+    }
+
+    private static void dropDraggedItem(
+            LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
+            Point endPoint, long downTime, Point itemVisibleCenter, Rect itemVisibleBounds,
+            String itemLabel, String expectedExistingPackageName) {
+        LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen before drop "
+                + itemVisibleCenter + " in " + itemVisibleBounds);
+
+        launchable.executeAndWaitForWindowChange(() -> {
+            launcher.sendPointer(
+                    downTime,
+                    SystemClock.uptimeMillis(),
+                    MotionEvent.ACTION_UP,
+                    endPoint,
+                    LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER);
+            LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: after "
+                    + "drop");
+        }, itemLabel, "dropping taskbar item");
+
+        try (LauncherInstrumentation.Closable c = launcher.addContextLayer("dropped item")) {
+            launchable.assertAppLaunched(itemLabel, By.pkg(expectedNewPackageName));
+            launcher.checkPackagesVisible(
+                    new String[] {expectedNewPackageName, expectedExistingPackageName});
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f063191..8e0eb7b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -84,6 +84,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
@@ -123,7 +124,8 @@
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
     public enum ContainerType {
-        WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP
+        WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP,
+        TASKBAR_ALL_APPS
     }
 
     public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
@@ -167,6 +169,7 @@
     private static final String OVERVIEW_RES_ID = "overview_panel";
     private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
     private static final String CONTEXT_MENU_RES_ID = "popup_container";
+    private static final String TASKBAR_RES_ID = "taskbar_view";
     public static final int WAIT_TIME_MS = 60000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     private static final String ANDROID_PACKAGE = "android";
@@ -500,6 +503,16 @@
         }
     }
 
+    void checkPackagesVisible(String[] expectedVisiblePackages) {
+        Set<String> actualVisiblePackages =
+                getVisiblePackagesStream().collect(Collectors.toSet());
+
+        for (String expectedVisible : expectedVisiblePackages) {
+            assertTrue("Expected package not visible: " + expectedVisible,
+                    actualVisiblePackages.contains(expectedVisible));
+        }
+    }
+
     private String getVisiblePackages() {
         final String apps = getVisiblePackagesStream().collect(Collectors.joining(", "));
         return !apps.isEmpty()
@@ -722,27 +735,41 @@
                     waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
                     return waitForLauncherObject(WORKSPACE_RES_ID);
                 }
                 case WIDGETS: {
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
+                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
                     return waitForLauncherObject(WIDGETS_RES_ID);
                 }
+                case TASKBAR_ALL_APPS:
                 case HOME_ALL_APPS: {
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
                     return waitForLauncherObject(APPS_RES_ID);
                 }
                 case OVERVIEW: {
                     waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
                     return waitForLauncherObject(OVERVIEW_RES_ID);
                 }
                 case FALLBACK_OVERVIEW: {
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
+                    waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+                    waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+                    waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
                     return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
                 }
                 case LAUNCHED_APP: {
@@ -750,6 +777,12 @@
                     waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+
+                    if (isTablet() && !isFallbackOverview()) {
+                        waitForLauncherObject(TASKBAR_RES_ID);
+                    } else {
+                        waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+                    }
                     return null;
                 }
                 default:
@@ -844,11 +877,22 @@
     }
 
     /**
+     * @deprecated use goHome().
      * Presses nav bar home button.
      *
      * @return the Workspace object.
      */
+    @Deprecated
     public Workspace pressHome() {
+        return goHome();
+    }
+
+    /**
+     * Presses nav bar home button.
+     *
+     * @return the Workspace object.
+     */
+    public Workspace goHome() {
         try (LauncherInstrumentation.Closable e = eventsCheck();
              LauncherInstrumentation.Closable c = addContextLayer("want to switch to home")) {
             waitForLauncherInitialized();
@@ -863,8 +907,13 @@
                 setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
 
                 final Point displaySize = getRealDisplaySize();
+                // The swipe up to home gesture starts from inside the launcher when the user is
+                // already home. Otherwise, the gesture can start inside the launcher process if the
+                // taskbar is visible.
                 boolean gestureStartFromLauncher = isTablet()
-                        ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
+                        ? !isLauncher3()
+                        || hasLauncherObject(WORKSPACE_RES_ID)
+                        || hasLauncherObject(TASKBAR_RES_ID)
                         : isLauncherVisible();
 
                 // CLose floating views before going back to home.
@@ -1107,11 +1156,13 @@
         }
     }
 
-    @NonNull UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
+    @NonNull
+    UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
         return waitForObjectsInContainer(container, selector).get(0);
     }
 
-    @NonNull List<UiObject2> waitForObjectsInContainer(
+    @NonNull
+    List<UiObject2> waitForObjectsInContainer(
             UiObject2 container, BySelector selector) {
         try {
             final List<UiObject2> objects = container.wait(
@@ -1299,9 +1350,7 @@
     }
 
     void scrollToLastVisibleRow(
-            UiObject2 container,
-            Collection<UiObject2> items,
-            int topPaddingInContainer) {
+            UiObject2 container, Collection<UiObject2> items, int topPaddingInContainer) {
         final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
                 Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top));
 
@@ -1324,8 +1373,8 @@
                         containerRect.height() - distance - bottomGestureMarginInContainer,
                         0,
                         bottomGestureMarginInContainer),
-                10,
-                true);
+                /* steps= */ 10,
+                /* slowDown= */ true);
     }
 
     void scrollLeftByDistance(UiObject2 container, int distance) {
@@ -1406,13 +1455,13 @@
         final Point end = new Point(endX, endY);
         sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
         final long endTime = movePointer(
-                start, end, steps, false, downTime, slowDown, gestureScope);
+                start, end, steps, false, downTime, downTime, slowDown, gestureScope);
         sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope);
     }
 
-    long movePointer(Point start, Point end, int steps, boolean isDecelerating,
-            long downTime, boolean slowDown, GestureScope gestureScope) {
-        long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS,
+    long movePointer(Point start, Point end, int steps, boolean isDecelerating, long downTime,
+            long startTime, boolean slowDown, GestureScope gestureScope) {
+        long endTime = movePointer(downTime, startTime, steps * GESTURE_STEP_MS,
                 isDecelerating, start, end, gestureScope);
         if (slowDown) {
             endTime = movePointer(downTime, endTime + GESTURE_STEP_MS, 5 * GESTURE_STEP_MS, end,
@@ -1648,6 +1697,29 @@
         getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
     }
 
+    /**
+     * Reloads the workspace with a test layout that includes the Test Activity app icon on the
+     * hotseat.
+     */
+    public void useTestWorkspaceLayoutOnReload() {
+        getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
+    }
+
+    /** Reloads the workspace with the default layout defined by the user's grid size selection. */
+    public void useDefaultWorkspaceLayoutOnReload() {
+        getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
+    }
+
+    /** Shows the taskbar if it is hidden, otherwise does nothing. */
+    public void showTaskbarIfHidden() {
+        getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);
+    }
+
+    public List<String> getHotseatIconNames() {
+        return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
+                .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     private String[] getActivities() {
         return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES)
                 .getStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD);
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
new file mode 100644
index 0000000..ce1c3c0
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
@@ -0,0 +1,39 @@
+/*
+ * 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.tapl;
+
+/** Launchable that can serve as a source for dragging and dropping to splitscreen. */
+interface SplitscreenDragSource {
+
+    /**
+     * Drags this app icon to the left (landscape) or bottom (portrait) of the screen, launching it
+     * in splitscreen.
+     *
+     * @param expectedNewPackageName package name of the app being dragged
+     * @param expectedExistingPackageName package name of the already-launched app
+     */
+    default void dragToSplitscreen(
+            String expectedNewPackageName, String expectedExistingPackageName) {
+        Launchable launchable = getLaunchable();
+        LauncherInstrumentation launcher = launchable.mLauncher;
+        try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
+            LaunchedAppState.dragToSplitscreen(
+                    launcher, launchable, expectedNewPackageName, expectedExistingPackageName);
+        }
+    }
+
+    Launchable getLaunchable();
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
new file mode 100644
index 0000000..b5a08c3
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -0,0 +1,123 @@
+/*
+ * 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.tapl;
+
+import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Operations on the Taskbar from LaunchedApp.
+ */
+public final class Taskbar {
+
+    private final LauncherInstrumentation mLauncher;
+
+    Taskbar(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+    }
+
+    /**
+     * Returns an app icon with the given name. This fails if the icon is not found.
+     */
+    @NonNull
+    public TaskbarAppIcon getAppIcon(String appName) {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get a taskbar icon")) {
+            return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
+                    mLauncher.waitForLauncherObject("taskbar_view"),
+                    AppIcon.getAppIconSelector(appName, mLauncher)));
+        }
+    }
+
+    /**
+     * Hides this taskbar.
+     *
+     * The taskbar must already be visible when calling this method.
+     */
+    public void hide() {
+        mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to hide the taskbar");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            mLauncher.waitForLauncherObject("taskbar_view");
+
+            final long downTime = SystemClock.uptimeMillis();
+            Point stashTarget = new Point(
+                    mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
+
+            mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
+                    LauncherInstrumentation.GestureScope.INSIDE);
+            LauncherInstrumentation.log("hideTaskbar: sent down");
+
+            try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+                mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+                mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
+                        LauncherInstrumentation.GestureScope.INSIDE);
+            }
+        } finally {
+            mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+        }
+    }
+
+    /**
+     * Opens the Taskbar all apps page.
+     */
+    public AllAppsFromTaskbar openAllApps() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to open taskbar all apps");
+             LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+
+            mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
+                    mLauncher.waitForLauncherObject("taskbar_view"), getAllAppsButtonSelector()));
+
+            return new AllAppsFromTaskbar(mLauncher);
+        }
+    }
+
+    /** Returns a list of app icon names on the Taskbar */
+    public List<String> getIconNames() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get all taskbar icons")) {
+            return mLauncher.waitForObjectsInContainer(
+                    mLauncher.waitForLauncherObject("taskbar_view"),
+                    AppIcon.getAnyAppIconSelector())
+                    .stream()
+                    .map(UiObject2::getText)
+                    .filter(text -> !TextUtils.isEmpty(text)) // Filter out the all apps button
+                    .collect(Collectors.toList());
+        }
+    }
+
+    private static BySelector getAllAppsButtonSelector() {
+        // Look for an icon with no text
+        return By.clazz(TextView.class).text("");
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
new file mode 100644
index 0000000..099acd4
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon specifically on the Taskbar.
+ */
+public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSource {
+
+    private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+    TaskbarAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+        super(launcher, icon);
+    }
+
+    @Override
+    protected Pattern getLongClickEvent() {
+        return LONG_CLICK_EVENT;
+    }
+
+    @Override
+    public TaskbarAppIconMenu openDeepShortcutMenu() {
+        return (TaskbarAppIconMenu) super.openDeepShortcutMenu();
+    }
+
+    @Override
+    protected TaskbarAppIconMenu createMenu(UiObject2 menu) {
+        return new TaskbarAppIconMenu(mLauncher, menu);
+    }
+
+    @Override
+    public Launchable getLaunchable() {
+        return this;
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java
new file mode 100644
index 0000000..1f137c5
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java
@@ -0,0 +1,38 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Context menu of a Taskbar app icon.
+ */
+public final class TaskbarAppIconMenu extends AppIconMenu {
+
+    TaskbarAppIconMenu(LauncherInstrumentation launcher, UiObject2 deepShortcutsContainer) {
+        super(launcher, deepShortcutsContainer);
+    }
+
+    @Override
+    public TaskbarAppIconMenuItem getMenuItem(String shortcutText) {
+        return (TaskbarAppIconMenuItem) super.getMenuItem(shortcutText);
+    }
+
+    @Override
+    protected TaskbarAppIconMenuItem createMenuItem(UiObject2 menuItem) {
+        return new TaskbarAppIconMenuItem(mLauncher, menuItem);
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
new file mode 100644
index 0000000..69a8a08
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
@@ -0,0 +1,57 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.regex.Pattern;
+
+/**
+ * Menu item in a Taskbar app icon menu.
+ */
+public final class TaskbarAppIconMenuItem extends AppIconMenuItem implements SplitscreenDragSource {
+
+    private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+    TaskbarAppIconMenuItem(
+            LauncherInstrumentation launcher, UiObject2 shortcut) {
+        super(launcher, shortcut);
+    }
+
+    @Override
+    protected void addExpectedEventsForLongClick() {
+        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
+    }
+
+    @Override
+    protected void waitForLongPressConfirmation() {
+        // On long-press, the popup container closes and the system drag-and-drop begins. This
+        // only leaves launcher views that were previously visible.
+        mLauncher.waitUntilLauncherObjectGone("popup_container");
+    }
+
+    @Override
+    protected String launchableType() {
+        return "taskbar app icon menu item";
+    }
+
+    @Override
+    public Launchable getLaunchable() {
+        return this;
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 73e9830..2346249 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -39,8 +39,8 @@
     }
 
     @Override
-    protected String getLongPressIndicator() {
-        return "drop_target_bar";
+    protected void waitForLongPressConfirmation() {
+        mLauncher.waitForLauncherObject("drop_target_bar");
     }
 
     @Override
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 4f4ce30..fee4490 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -178,10 +178,10 @@
      * pageDelta: 3, the latest page that can be created is 2) the icon will be dragged onto the
      * page that can be created and is closest to the target page.
      *
-     * @param homeAppIcon   - icon to drag.
-     * @param pageDelta - how many pages should the icon be dragged from the current page.
-     *                  It can be a negative value. currentPage + pageDelta should be greater
-     *                  than or equal to 0.
+     * @param homeAppIcon - icon to drag.
+     * @param pageDelta   - how many pages should the icon be dragged from the current page.
+     *                    It can be a negative value. currentPage + pageDelta should be greater
+     *                    than or equal to 0.
      */
     public void dragIcon(HomeAppIcon homeAppIcon, int pageDelta) {
         if (mHotseat.getVisibleBounds().height() > mHotseat.getVisibleBounds().width()) {
@@ -205,7 +205,6 @@
                 mLauncher,
                 homeAppIcon,
                 new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
-                "popup_container",
                 false,
                 false,
                 () -> mLauncher.expectEvent(
@@ -244,12 +243,11 @@
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "removing app icon from workspace")) {
             dragIconToWorkspace(
-                    mLauncher, homeAppIcon,
+                    mLauncher,
+                    homeAppIcon,
                     () -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
-                    true, /* decelerating */
-                    homeAppIcon.getLongPressIndicator(),
                     () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
-                    null);
+                    /* expectDropEvents= */ null);
 
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "dragged the app to the drop bar")) {
@@ -261,8 +259,9 @@
     /**
      * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
      *
-     * @param launcher the root TAPL instrumentation object of {@link LauncherInstrumentation} type.
-     * @param homeAppIcon to be uninstalled.
+     * @param launcher              the root TAPL instrumentation object of {@link
+     *                              LauncherInstrumentation} type.
+     * @param homeAppIcon           to be uninstalled.
      * @param launcher              the root TAPL instrumentation object of {@link
      *                              LauncherInstrumentation} type.
      * @param homeAppIcon           to be uninstalled.
@@ -274,12 +273,11 @@
         try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
                 "uninstalling app icon")) {
             dragIconToWorkspace(
-                    launcher, homeAppIcon,
+                    launcher,
+                    homeAppIcon,
                     () -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
-                    true, /* decelerating */
-                    homeAppIcon.getLongPressIndicator(),
                     expectLongClickEvents,
-                    null);
+                    /* expectDropEvents= */null);
 
             launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
 
@@ -346,37 +344,43 @@
     }
 
     static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
-            Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
+            Point dest, boolean startsActivity, boolean isWidgetShortcut,
             Runnable expectLongClickEvents) {
         Runnable expectDropEvents = null;
         if (startsActivity || isWidgetShortcut) {
             expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
                     LauncherInstrumentation.EVENT_START);
         }
-        dragIconToWorkspace(launcher, launchable, () -> dest, false, longPressIndicator,
-                expectLongClickEvents, expectDropEvents);
+        dragIconToWorkspace(
+                launcher, launchable, () -> dest, expectLongClickEvents, expectDropEvents);
     }
 
     /**
-     * Drag icon in workspace to else where.
+     * Drag icon in workspace to else where and drop it immediately.
+     * (There is no slow down time before drop event)
      * This function expects the launchable is inside the workspace and there is no drop event.
      */
-    static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
-            Supplier<Point> destSupplier, String longPressIndicator) {
-        dragIconToWorkspace(launcher, launchable, destSupplier, false, longPressIndicator,
-                () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
+    static void dragIconToWorkspace(
+            LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> destSupplier) {
+        dragIconToWorkspace(
+                launcher,
+                launchable,
+                destSupplier,
+                () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+                /* expectDropEvents= */ null);
     }
 
     static void dragIconToWorkspace(
-            LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
-            boolean isDecelerating, String longPressIndicator, Runnable expectLongClickEvents,
+            LauncherInstrumentation launcher,
+            Launchable launchable,
+            Supplier<Point> dest,
+            Runnable expectLongClickEvents,
             @Nullable Runnable expectDropEvents) {
         try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
                 "want to drag icon to workspace")) {
             final long downTime = SystemClock.uptimeMillis();
             Point dragStart = launchable.startDrag(
                     downTime,
-                    longPressIndicator,
                     expectLongClickEvents,
                     /* runToSpringLoadedState= */ true);
             Point targetDest = dest.get();
@@ -391,7 +395,7 @@
                 Point finalDragStart = dragStart;
                 executeAndWaitForPageScroll(launcher,
                         () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS,
-                                isDecelerating, downTime, true,
+                                true, downTime, downTime, true,
                                 LauncherInstrumentation.GestureScope.INSIDE));
                 targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
                 dragStart = screenEdge;
@@ -399,8 +403,9 @@
 
             // targetDest.x is now between 0 and displayX so we found the target page,
             // we just have to put move the icon to the destination and drop it
-            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
-                    downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
+                    downTime, SystemClock.uptimeMillis(), false,
+                    LauncherInstrumentation.GestureScope.INSIDE);
             dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
         }
     }
@@ -488,4 +493,4 @@
             return widget != null ? new Widget(mLauncher, widget) : null;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
index 93c2213..d8d4420 100644
--- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
@@ -41,7 +41,6 @@
                                     ? launchableCenter.x - width / 2
                                     : launchableCenter.x + width / 2,
                             displaySize.y / 2),
-                    launchable.getLongPressIndicator(),
                     startsActivity,
                     isWidgetShortcut,
                     launchable::addExpectedEventsForLongClick);