Merge "Sync hotseat/taskbar handoff" into tm-dev
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/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/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/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/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..6aa4883 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>
@@ -76,9 +88,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:gravity">start</item>
     </style>
 
     <style name="TextAppearance.GestureTutorial.Feedback.Subtext.Dark"
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 8fb085d..6f0f993 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -26,7 +26,6 @@
 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.NO_BUTTON;
 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;
@@ -38,7 +37,6 @@
 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;
@@ -46,7 +44,6 @@
 import android.os.IBinder;
 import android.view.Display;
 import android.view.View;
-import android.view.WindowInsets;
 import android.window.SplashScreen;
 
 import androidx.annotation.Nullable;
@@ -295,8 +292,8 @@
         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(DisplayController.getNavigationMode(this));
@@ -614,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 (DisplayController.getNavigationMode(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/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index c4a3fb5..8f8ac8e 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -579,8 +579,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 +590,7 @@
                 if (scrimEnabled) {
                     mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT);
                 }
-                mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
+                mLauncher.resumeExpensiveViewUpdates();
             };
         }
 
@@ -1186,6 +1186,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.
@@ -1580,7 +1593,8 @@
                 View launcherView = findLauncherView(appTargets);
                 boolean playFallBackAnimation = (launcherView == null
                         && launcherIsForceInvisibleOrOpening)
-                        || mLauncher.getWorkspace().isOverlayShown();
+                        || mLauncher.getWorkspace().isOverlayShown()
+                        || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
 
                 boolean playWorkspaceReveal = true;
                 boolean skipAllAppsScale = false;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e949b23..5387b1a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -49,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;
 
@@ -185,8 +186,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),
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 cc462c1..5ce4fa7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 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;
@@ -74,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;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 6bc4c0a..473be9e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -250,7 +250,11 @@
      * 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(FLAG_IN_APP);
     }
 
     /**
@@ -266,8 +270,15 @@
                 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;
     }
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/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index f354b2c..1320060 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;
@@ -143,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() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index b36b9f1..37cd753 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -25,6 +25,9 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.BaseAdapterProvider;
+import com.android.launcher3.allapps.BaseAllAppsAdapter;
 import com.android.launcher3.allapps.BaseAllAppsContainerView;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
 
@@ -79,4 +82,11 @@
         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/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 9240fb8..2f8e4d9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -20,7 +20,6 @@
 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;
@@ -37,20 +36,6 @@
     }
 
     /**
-     * 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) {
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/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index e5c7b0e..895cf89 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -35,11 +35,11 @@
 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.
@@ -51,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;
@@ -184,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);
         }
@@ -245,31 +207,31 @@
      */
     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);
         }
 
@@ -368,7 +330,7 @@
                                 true);
                     }
                 } else {
-                    mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
+                    mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
                             true);
                 }
                 break;
@@ -387,7 +349,7 @@
                                 true);
                     }
                 } else {
-                    mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
+                    mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
                             true);
                 }
                 mLastRectTouched = null;
@@ -403,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();
@@ -430,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/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index db92e33..3e7ad62 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -135,8 +135,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);
 
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 783c868..57b42f8 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -49,9 +49,9 @@
 
 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;
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/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java
index dbe260a..7836ece 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 = context.getResources().getDisplayMetrics().widthPixels;
+        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/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/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/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/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 9a101b9..b70c411 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -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..2ddbd97 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;
@@ -73,7 +74,7 @@
     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;
@@ -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);
@@ -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() {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 2fd7cde..4b836e3 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -20,7 +20,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.graphics.Insets;
 import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
@@ -41,6 +40,7 @@
 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.quickstep.interaction.TutorialController.TutorialType;
@@ -67,7 +67,9 @@
 
     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);
@@ -139,22 +141,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 +300,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);
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/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index d85515a..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;
@@ -82,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
@@ -196,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;
@@ -388,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) {
@@ -479,11 +491,12 @@
             if (IS_VERBOSE) {
                 String name = (event instanceof Enum) ? ((Enum) event).name() :
                         event.getId() + "";
-
-                Log.d(TAG, mInstanceId == DEFAULT_INSTANCE_ID
-                        ? String.format("\n%s = %dms\n", name, mLatencyInMillis)
-                        : String.format("\n%s = %dms (InstanceId:%s)\n", name,
-                                mLatencyInMillis, mInstanceId));
+                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,
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/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..21e3ea0 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;
@@ -27,9 +28,11 @@
 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 +48,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 +63,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 +77,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 +92,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 +104,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.topActivity.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,18 +131,32 @@
         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}
@@ -156,7 +188,7 @@
                         splitRatio, adapter);
             } else {
                 mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
-                        new Intent(), taskId2, mainOpts.toBundle(), null /* sideOptions */,
+                        fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
                         stagePosition, splitRatio, adapter);
             }
         }
@@ -250,7 +282,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 +294,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/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 04af3c1..d9f668d 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
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5e331e2..6bb20fc 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;
@@ -2013,22 +2008,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 +2261,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;
         }
@@ -3973,28 +3950,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;
         }
@@ -4039,8 +4005,8 @@
         mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
                 secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isInitialSplit */);
         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;
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/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 6dc9f8d..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,6 +37,10 @@
 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.SystemUiProxy;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -142,29 +145,34 @@
             LauncherModelHelper helper = new LauncherModelHelper();
             try {
                 helper.sandboxContext.allow(SystemUiProxy.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);
@@ -172,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/values/config.xml b/res/values/config.xml
index 1c996eb..509f363 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,6 +69,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" />
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index b47554f..f53d01e 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -29,7 +29,6 @@
 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;
@@ -41,8 +40,6 @@
 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;
@@ -322,11 +319,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
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index cd9bbf7..1b333d8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -75,6 +75,7 @@
     public final int heightPx;
     public final int availableWidthPx;
     public final int availableHeightPx;
+    public final int rotationHint;
 
     public final float aspectRatio;
 
@@ -239,6 +240,7 @@
         this.isGestureMode = isGestureMode;
         windowX = windowBounds.bounds.left;
         windowY = windowBounds.bounds.top;
+        this.rotationHint = windowBounds.rotationHint;
 
         isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
 
@@ -346,9 +348,14 @@
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
         hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
-        isQsbInline = isLargeTablet && isLandscape && hotseatQsbHeight > 0;
+        // Whether QSB might be inline in appropriate orientation (landscape).
+        boolean canQsbInline = isLargeTablet && hotseatQsbHeight > 0;
+        isQsbInline = canQsbInline && isLandscape;
 
-        if (isTaskbarPresent && !isGestureMode && isQsbInline) {
+        // 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 =
@@ -543,8 +550,8 @@
     }
 
     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)
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 886c657..25fd643 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -55,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;
@@ -180,8 +181,7 @@
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
 
     @VisibleForTesting
-    public InvariantDeviceProfile() {
-    }
+    public InvariantDeviceProfile() { }
 
     @TargetApi(23)
     private InvariantDeviceProfile(Context context) {
@@ -278,11 +278,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;
@@ -613,10 +618,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;
 
@@ -626,6 +635,8 @@
             if (diff < minDiff) {
                 minDiff = diff;
                 bestMatch = profile;
+            } else if (diff == minDiff && profile.rotationHint == rotation) {
+                bestMatch = profile;
             }
         }
         return bestMatch;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8fb2f53..07d7b90 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -228,6 +228,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;
@@ -1108,21 +1109,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 +1154,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,
@@ -3192,4 +3196,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/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/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/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 114f813..b94a612 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -256,4 +256,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..f542d8e 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;
@@ -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..fd8945a 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -33,7 +33,7 @@
 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;
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 4a886a4..cb459ea 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -38,9 +38,9 @@
 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;
 
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/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2459e09..cc17064 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,
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 308a8f2..5ece4a6 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -440,7 +440,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 0c1ba8b..f392e95 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -527,7 +527,11 @@
         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 scrolled on one of the all apps surfaces such as A-Z list, search "
+                + "result page etc.")
+        LAUNCHER_ALLAPPS_SCROLLED(985);
 
         // ADD MORE
 
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/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 2bf3b14..05fe182 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -125,7 +125,6 @@
     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";
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index f944d3c..22e3de8 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -26,10 +26,8 @@
 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.PackageManagerHelper.getPackageFilter;
-import static com.android.launcher3.util.WindowManagerCompat.MIN_LARGE_TABLET_WIDTH;
-import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
-
-import static java.util.Collections.emptyMap;
+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;
@@ -38,12 +36,13 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 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;
@@ -52,11 +51,12 @@
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.uioverrides.ApiWrapper;
+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;
 
@@ -89,6 +89,7 @@
 
     // 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<>();
@@ -115,23 +116,9 @@
         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());
-    }
-
-    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;
+                wmProxy, wmProxy.estimateInternalDisplayBounds(context));
     }
 
     /**
@@ -226,16 +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
                 || 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;
@@ -254,9 +242,8 @@
         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"
@@ -264,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;
             }
@@ -290,30 +279,38 @@
 
     public static class Info {
 
-        // 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;
@@ -321,54 +318,29 @@
             mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
             navigationMode = parseNavigationMode(context);
 
-            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.
          */
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/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 873a518..0000000
--- a/src/com/android/launcher3/util/WindowManagerCompat.java
+++ /dev/null
@@ -1,109 +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;
-    public static final int MIN_LARGE_TABLET_WIDTH = 720;
-
-    /**
-     * 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/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/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 1c4ca8c..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;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index daa8fb7..6e97774 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;
@@ -94,13 +97,17 @@
             "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 +178,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 +269,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_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/src/com/android/launcher3/DeviceProfileTest.kt b/tests/src/com/android/launcher3/DeviceProfileTest.kt
index 60046a0..75ad21d 100644
--- a/tests/src/com/android/launcher3/DeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileTest.kt
@@ -139,7 +139,7 @@
         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)
@@ -153,7 +153,7 @@
         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)
@@ -167,7 +167,7 @@
         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(true)
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());
         }