Merge "Do not animate statusbar unfold when in portrait orientation" into sc-v2-dev
diff --git a/quickstep/res/drawable/default_sandbox_mock_launcher.xml b/quickstep/res/drawable/default_sandbox_mock_launcher.xml
deleted file mode 100644
index 38fbcf0..0000000
--- a/quickstep/res/drawable/default_sandbox_mock_launcher.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="360dp"
-    android:height="146dp"
-    android:viewportWidth="360"
-    android:viewportHeight="146">
-  <path
-      android:pathData="M25,96L335,96A25,25 0,0 1,360 121L360,121A25,25 0,0 1,335 146L25,146A25,25 0,0 1,0 121L0,121A25,25 0,0 1,25 96z"
-      android:fillColor="#3C4043"/>
-  <path
-      android:pathData="M30,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#8AB4F8"/>
-  <path
-      android:pathData="M130,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#F28B82"/>
-  <path
-      android:pathData="M230,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#FDD663"/>
-  <path
-      android:pathData="M330,30m-30,0a30,30 0,1 1,60 0a30,30 0,1 1,-60 0"
-      android:fillColor="#81C995"/>
-</vector>
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
new file mode 100644
index 0000000..34bd4e2
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_conversation_top_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="126dp"
+            android:layout_marginEnd="548dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="300dp"
+            android:layout_marginEnd="16dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/top_bar_button"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_bar_button"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="300dp"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_conversation_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_conversation_background"
+        android:paddingBottom="80dp"
+
+        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:paddingBottom="@dimen/gesture_tutorial_message_input_margin_top"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/message_bar"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent">
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_1"
+                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"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/reply_icon_1"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/reply_icon_1"
+                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"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_conversation_profile_icon"
+                app:layout_constraintDimensionRatio="1:1"
+                app:layout_constraintBottom_toTopOf="@id/message_2"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginStart="17dp"
+                android:layout_marginEnd="441dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_received_message"
+                app:layout_constraintTop_toTopOf="@id/reply_icon_1"
+                app:layout_constraintBottom_toBottomOf="@id/reply_icon_1"
+                app:layout_constraintStart_toEndOf="@id/reply_icon_1"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_2"
+                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"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/message_3"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/message_3"
+                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"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toTopOf="@id/reply_icon_2"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:id="@+id/reply_icon_2"
+                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"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
+                app:cardBackgroundColor="@color/mock_conversation_profile_icon"
+                app:layout_constraintDimensionRatio="1:1"
+                app:layout_constraintBottom_toTopOf="@id/message_4"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                android:layout_width="0dp"
+                android:layout_height="36dp"
+                android:layout_marginStart="17dp"
+                android:layout_marginEnd="473dp"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_received_message"
+                app:layout_constraintTop_toTopOf="@id/reply_icon_2"
+                app:layout_constraintBottom_toBottomOf="@id/reply_icon_2"
+                app:layout_constraintStart_toEndOf="@id/reply_icon_2"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+            <androidx.cardview.widget.CardView
+                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"
+
+                app:cardElevation="0dp"
+                app:cardCornerRadius="18dp"
+                app:cardBackgroundColor="@color/mock_conversation_sent_message"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/message_bar"
+            android:layout_width="0dp"
+            android:layout_height="44dp"
+            android:layout_marginTop="36dp"
+            android:layout_marginBottom="24dp"
+            android:layout_marginStart="134dp"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="164dp"
+            app:cardBackgroundColor="@color/mock_conversation_message_input"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="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_foldable_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
new file mode 100644
index 0000000..0309cc3
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_list_top_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="43dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="126dp"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="4dp"
+            app:cardBackgroundColor="@color/mock_list_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_list_background"
+        android:paddingBottom="80dp"
+
+        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="0dp"
+            android:layout_height="0dp"
+            android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
+            android:paddingStart="126dp"
+
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/mock_button">
+
+            <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_constraintStart_toStartOf="parent"/>
+
+            <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="270dp"
+
+                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_2"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="110dp"
+                android:layout_marginTop="4dp"
+
+                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_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="@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_line_3"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="243dp"
+
+                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_4"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="154dp"
+                android:layout_marginTop="4dp"
+
+                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_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="@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_line_5"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="251dp"
+
+                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_6"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="15dp"
+                android:layout_marginTop="4dp"
+
+                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_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="@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_line_7"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="227dp"
+
+                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_8"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="72dp"
+                android:layout_marginTop="4dp"
+
+                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_icon_5"
+                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="@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_line_9"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="297dp"
+
+                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_10"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="111dp"
+                android:layout_marginTop="4dp"
+
+                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_icon_6"
+                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="@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_line_11"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="230dp"
+
+                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_12"
+                android:layout_width="0dp"
+                android:layout_height="16dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="72dp"
+                android:layout_marginTop="4dp"
+
+                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_icon_7"
+                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="@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_line_13"
+                android:layout_width="0dp"
+                android:layout_height="18dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
+                android:layout_marginEnd="242dp"
+
+                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="219dp"
+                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>
+
+        <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_foldable_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
new file mode 100644
index 0000000..5612666
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_hotseat.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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">
+
+    <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_webpage.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml
new file mode 100644
index 0000000..67e9b02
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<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="match_parent"
+    android:background="@color/mock_webpage_background">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/url_bar"
+        android:layout_width="match_parent"
+        android:layout_height="101dp"
+        android:background="@color/mock_webpage_url_bar"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="48dp"
+            android:layout_marginBottom="16dp"
+            android:layout_marginStart="100dp"
+            android:layout_marginEnd="100dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="100dp"
+            app:cardBackgroundColor="@color/mock_webpage_url_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="88dp"
+        android:layout_marginStart="100dp"
+        android:layout_marginEnd="100dp"
+        android:background="@color/mock_webpage_top_bar"
+
+        app:layout_constraintTop_toBottomOf="@id/url_bar"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/top_bar_button"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="22dp"
+            android:layout_marginBottom="22dp"
+            android:layout_marginStart="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="8dp"
+            app:cardBackgroundColor="@color/mock_webpage_top_bar_item"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginBottom="28dp"
+            android:layout_marginStart="97dp"
+            android:layout_marginEnd="325dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="2dp"
+            app:cardBackgroundColor="@color/mock_webpage_top_bar_item"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="@color/mock_webpage_background"
+        android:paddingTop="@dimen/gesture_tutorial_webpage_padding_top"
+        android:paddingStart="124dp"
+        android:paddingEnd="100dp"
+
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_1"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginEnd="126dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_2"
+            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"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_1"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_3"
+            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"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_2"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_button"
+            android:layout_width="47dp"
+            android:layout_height="12dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_3"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="47dp"
+            android:layout_height="12dp"
+            android:background="@color/mock_webpage_page_text"
+            android:layout_marginStart="11dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_3"
+            app:layout_constraintStart_toEndOf="@id/mock_button"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_block"
+            android:layout_width="0dp"
+            android:layout_height="240dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="24dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_large_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_button"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_4"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="52dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_block"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_5"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="41dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_4"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_6"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="71dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_7"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="198dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_6"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/mock_line_8"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
+            android:layout_marginEnd="64dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_7"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.cardview.widget.CardView
+            android:layout_width="0dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
+            android:layout_marginEnd="71dp"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
+            app:cardBackgroundColor="@color/mock_webpage_page_text"
+            app:layout_constraintTop_toBottomOf="@id/mock_line_8"
+            app:layout_constraintStart_toStartOf="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_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 0f01190..41d0a1d 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -25,13 +25,12 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <ImageView
+        <FrameLayout
             android:id="@+id/gesture_tutorial_fake_hotseat_view"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_centerHorizontal="true"
-            android:layout_alignParentBottom="true"
-            android:layout_marginBottom="70dp"/>
+            android:layout_alignParentBottom="true"/>
 
     </RelativeLayout>
 
@@ -128,8 +127,6 @@
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         android:layout_centerHorizontal="true"
-        android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
-        android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
         android:layout_marginTop="24dp"
         android:paddingTop="24dp"
         android:paddingBottom="16dp"
diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
index 9951663..e8d5d79 100644
--- a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
+++ b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml
@@ -94,7 +94,7 @@
         <androidx.constraintlayout.widget.ConstraintLayout
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:paddingBottom="36dp"
+            android:paddingBottom="@dimen/gesture_tutorial_message_input_margin_top"
 
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toTopOf="@id/message_bar"
@@ -105,9 +105,9 @@
                 android:id="@+id/message_1"
                 android:layout_width="0dp"
                 android:layout_height="112dp"
-                android:layout_marginBottom="32dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
                 android:layout_marginStart="124dp"
-                android:layout_marginEnd="18dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -118,13 +118,13 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/reply_icon_1"
-                android:layout_width="44dp"
-                android:layout_height="44dp"
-                android:layout_marginBottom="32dp"
-                android:layout_marginStart="26dp"
+                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_message_padding_start"
 
                 app:cardElevation="0dp"
-                app:cardCornerRadius="100dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
                 app:cardBackgroundColor="@color/mock_conversation_profile_icon"
                 app:layout_constraintDimensionRatio="1:1"
                 app:layout_constraintBottom_toTopOf="@id/message_2"
@@ -148,9 +148,9 @@
                 android:id="@+id/message_2"
                 android:layout_width="0dp"
                 android:layout_height="36dp"
-                android:layout_marginBottom="4dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
                 android:layout_marginStart="280dp"
-                android:layout_marginEnd="18dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -163,9 +163,9 @@
                 android:id="@+id/message_3"
                 android:layout_width="0dp"
                 android:layout_height="74dp"
-                android:layout_marginBottom="32dp"
+                android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
                 android:layout_marginStart="124dp"
-                android:layout_marginEnd="18dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
@@ -176,13 +176,13 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/reply_icon_2"
-                android:layout_width="44dp"
-                android:layout_height="44dp"
-                android:layout_marginBottom="32dp"
-                android:layout_marginStart="26dp"
+                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_message_padding_start"
 
                 app:cardElevation="0dp"
-                app:cardCornerRadius="100dp"
+                app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
                 app:cardBackgroundColor="@color/mock_conversation_profile_icon"
                 app:layout_constraintDimensionRatio="1:1"
                 app:layout_constraintBottom_toTopOf="@id/message_4"
@@ -207,7 +207,7 @@
                 android:layout_width="0dp"
                 android:layout_height="74dp"
                 android:layout_marginStart="124dp"
-                android:layout_marginEnd="18dp"
+                android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
 
                 app:cardElevation="0dp"
                 app:cardCornerRadius="18dp"
diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
index ad6b165..364ad6d 100644
--- a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
+++ b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml
@@ -61,7 +61,7 @@
         <androidx.constraintlayout.widget.ConstraintLayout
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:paddingTop="28dp"
+            android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
             android:paddingStart="26dp"
             android:paddingBottom="14dp"
 
@@ -70,11 +70,11 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_1"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
+                android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
 
                 app:cardElevation="0dp"
-                app:cardCornerRadius="100dp"
+                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"/>
@@ -83,7 +83,7 @@
                 android:id="@+id/conversation_line_1"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="217dp"
 
                 app:cardElevation="0dp"
@@ -99,7 +99,7 @@
                 android:id="@+id/conversation_line_2"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="142dp"
                 android:layout_marginTop="4dp"
 
@@ -113,12 +113,12 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_2"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                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="100dp"
+                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"/>
@@ -127,7 +127,7 @@
                 android:id="@+id/conversation_line_3"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="190dp"
 
                 app:cardElevation="0dp"
@@ -143,7 +143,7 @@
                 android:id="@+id/conversation_line_4"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="171dp"
                 android:layout_marginTop="4dp"
 
@@ -157,12 +157,12 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_3"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                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="100dp"
+                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"/>
@@ -171,7 +171,7 @@
                 android:id="@+id/conversation_line_5"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="198dp"
 
                 app:cardElevation="0dp"
@@ -187,7 +187,7 @@
                 android:id="@+id/conversation_line_6"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="79dp"
                 android:layout_marginTop="4dp"
 
@@ -201,12 +201,12 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_4"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                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="100dp"
+                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"/>
@@ -215,7 +215,7 @@
                 android:id="@+id/conversation_line_7"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="174dp"
 
                 app:cardElevation="0dp"
@@ -231,7 +231,7 @@
                 android:id="@+id/conversation_line_8"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="117dp"
                 android:layout_marginTop="4dp"
 
@@ -245,12 +245,12 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_5"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                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="100dp"
+                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"/>
@@ -259,7 +259,7 @@
                 android:id="@+id/conversation_line_9"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="244dp"
 
                 app:cardElevation="0dp"
@@ -275,7 +275,7 @@
                 android:id="@+id/conversation_line_10"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="143dp"
                 android:layout_marginTop="4dp"
 
@@ -289,12 +289,12 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_6"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                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="100dp"
+                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"/>
@@ -303,7 +303,7 @@
                 android:id="@+id/conversation_line_11"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="177dp"
 
                 app:cardElevation="0dp"
@@ -319,7 +319,7 @@
                 android:id="@+id/conversation_line_12"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="117dp"
                 android:layout_marginTop="4dp"
 
@@ -333,12 +333,12 @@
 
             <androidx.cardview.widget.CardView
                 android:id="@+id/conversation_icon_7"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
+                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="100dp"
+                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"/>
@@ -347,7 +347,7 @@
                 android:id="@+id/conversation_line_13"
                 android:layout_width="0dp"
                 android:layout_height="18dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="189dp"
 
                 app:cardElevation="0dp"
@@ -363,7 +363,7 @@
                 android:id="@+id/conversation_line_14"
                 android:layout_width="0dp"
                 android:layout_height="16dp"
-                android:layout_marginStart="20dp"
+                android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
                 android:layout_marginEnd="166dp"
                 android:layout_marginTop="4dp"
 
diff --git a/quickstep/res/layout/gesture_tutorial_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_mock_hotseat.xml
new file mode 100644
index 0000000..b3e86cf
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_mock_hotseat.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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="70dp"
+    android:paddingStart="26dp"
+    android:paddingEnd="26dp">
+
+    <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_constraintHorizontal_chainStyle="spread_inside"
+        app:layout_constraintTop_toTopOf="parent"
+        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"
+
+        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_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_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_4"
+        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        android:layout_width="0dp"
+        android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
+        android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
+
+        app:cardElevation="0dp"
+        app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
+        app:cardBackgroundColor="@color/mock_search_bar"
+        app:layout_constraintTop_toBottomOf="@id/hotseat_icon_1"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_mock_webpage.xml b/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
index ab00a11..bb20968 100644
--- a/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
+++ b/quickstep/res/layout/gesture_tutorial_mock_webpage.xml
@@ -64,7 +64,6 @@
             android:layout_marginTop="22dp"
             android:layout_marginBottom="22dp"
             android:layout_marginStart="24dp"
-            android:layout_marginEnd="344dp"
 
             app:cardElevation="0dp"
             app:cardCornerRadius="8dp"
@@ -72,8 +71,7 @@
             app:layout_constraintDimensionRatio="1:1"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"/>
+            app:layout_constraintStart_toStartOf="parent"/>
 
         <androidx.cardview.widget.CardView
             android:layout_width="0dp"
@@ -97,9 +95,8 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:background="@color/mock_webpage_background"
-        android:paddingTop="32dp"
+        android:paddingTop="@dimen/gesture_tutorial_webpage_padding_top"
         android:paddingStart="24dp"
-        android:paddingBottom="50dp"
 
         app:layout_constraintTop_toBottomOf="@id/top_bar"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -109,11 +106,11 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_1"
             android:layout_width="0dp"
-            android:layout_height="36dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
             android:layout_marginEnd="126dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="4dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintStart_toStartOf="parent"
@@ -122,12 +119,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_2"
             android:layout_width="0dp"
-            android:layout_height="36dp"
-            android:layout_marginTop="8dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="64dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="4dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_1"
             app:layout_constraintStart_toStartOf="parent"
@@ -136,12 +133,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_3"
             android:layout_width="0dp"
-            android:layout_height="36dp"
-            android:layout_marginTop="8dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="151dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="4dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_2"
             app:layout_constraintStart_toStartOf="parent"
@@ -151,10 +148,10 @@
             android:id="@+id/mock_button"
             android:layout_width="47dp"
             android:layout_height="12dp"
-            android:layout_marginTop="8dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="4dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_3"
             app:layout_constraintStart_toStartOf="parent"/>
@@ -164,10 +161,10 @@
             android:layout_height="12dp"
             android:background="@color/mock_webpage_page_text"
             android:layout_marginStart="11dp"
-            android:layout_marginTop="8dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="4dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_3"
             app:layout_constraintStart_toEndOf="@id/mock_button"/>
@@ -176,11 +173,11 @@
             android:id="@+id/mock_block"
             android:layout_width="0dp"
             android:layout_height="240dp"
-            android:layout_marginTop="24dp"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
             android:layout_marginEnd="24dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="22dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_large_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_button"
             app:layout_constraintStart_toStartOf="parent"
@@ -189,12 +186,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_4"
             android:layout_width="0dp"
-            android:layout_height="22dp"
-            android:layout_marginTop="24dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
             android:layout_marginEnd="52dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="8dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_block"
             app:layout_constraintStart_toStartOf="parent"
@@ -203,12 +200,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_5"
             android:layout_width="0dp"
-            android:layout_height="22dp"
-            android:layout_marginTop="8dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="41dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="8dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_4"
             app:layout_constraintStart_toStartOf="parent"
@@ -217,12 +214,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_6"
             android:layout_width="0dp"
-            android:layout_height="22dp"
-            android:layout_marginTop="8dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="71dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="8dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_5"
             app:layout_constraintStart_toStartOf="parent"
@@ -231,12 +228,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_7"
             android:layout_width="0dp"
-            android:layout_height="22dp"
-            android:layout_marginTop="8dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="198dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="8dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_6"
             app:layout_constraintStart_toStartOf="parent"
@@ -245,12 +242,12 @@
         <androidx.cardview.widget.CardView
             android:id="@+id/mock_line_8"
             android:layout_width="0dp"
-            android:layout_height="22dp"
-            android:layout_marginTop="24dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
             android:layout_marginEnd="64dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="8dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_7"
             app:layout_constraintStart_toStartOf="parent"
@@ -258,12 +255,12 @@
 
         <androidx.cardview.widget.CardView
             android:layout_width="0dp"
-            android:layout_height="22dp"
-            android:layout_marginTop="8dp"
+            android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
+            android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
             android:layout_marginEnd="71dp"
 
             app:cardElevation="0dp"
-            app:cardCornerRadius="8dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
             app:cardBackgroundColor="@color/mock_webpage_page_text"
             app:layout_constraintTop_toBottomOf="@id/mock_line_8"
             app:layout_constraintStart_toStartOf="parent"
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 83ad9f3..3b1d217 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -30,6 +30,11 @@
         android:layout_gravity="bottom"
         android:clipChildren="false" />
 
+    <com.android.launcher3.taskbar.TaskbarScrimView
+        android:id="@+id/taskbar_scrim"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
     <FrameLayout
         android:id="@+id/navbuttons_view"
         android:layout_width="match_parent"
@@ -42,6 +47,7 @@
             android:layout_height="match_parent"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
             android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingTop="@dimen/taskbar_contextual_padding_top"
             android:gravity="center_vertical"
             android:layout_gravity="start"/>
 
@@ -62,6 +68,7 @@
             android:layout_height="match_parent"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
             android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingTop="@dimen/taskbar_contextual_padding_top"
             android:gravity="center_vertical"
             android:layout_gravity="end"/>
     </FrameLayout>
diff --git a/quickstep/res/layout/taskbar_contextual_button.xml b/quickstep/res/layout/taskbar_contextual_button.xml
index cbbbfab..4ffb8d8 100644
--- a/quickstep/res/layout/taskbar_contextual_button.xml
+++ b/quickstep/res/layout/taskbar_contextual_button.xml
@@ -15,9 +15,7 @@
 -->
 <ImageView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/taskbar_contextual_buttons_size"
-    android:layout_height="@dimen/taskbar_contextual_buttons_size"
-    android:layout_marginTop="@dimen/taskbar_contextual_button_margin"
-    android:paddingStart="@dimen/taskbar_nav_buttons_spacing"
+    android:layout_width="@dimen/taskbar_nav_buttons_size"
+    android:layout_height="@dimen/taskbar_nav_buttons_size"
     android:background="@drawable/taskbar_icon_click_feedback_roundrect"
     android:scaleType="center"/>
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 4755292..5edcc9d 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -41,6 +41,13 @@
     <color name="gesture_tutorial_action_button_label_color">#FF000000</color>
     <color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
 
+    <!-- Mock hotseat -->
+    <color name="mock_app_icon_1">#8AB4F8</color>
+    <color name="mock_app_icon_2">#F28B82</color>
+    <color name="mock_app_icon_3">#FDD663</color>
+    <color name="mock_app_icon_4">#81C995</color>
+    <color name="mock_search_bar">#3C4043</color>
+
     <!-- Mock conversation -->
     <color name="mock_conversation_background">#f1f3f4</color>
     <color name="mock_conversation_top_bar">#e8eaed</color>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index e903377..e08eda8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -117,13 +117,44 @@
     <dimen name="gestures_overscroll_finish_threshold">136dp</dimen>
 
     <!-- Tips Gesture Tutorial -->
-    <dimen name="gesture_tutorial_title_margin_start_end">40dp</dimen>
-    <dimen name="gesture_tutorial_subtitle_margin_start_end">16dp</dimen>
     <dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
-    <dimen name="gesture_tutorial_button_margin_start_end">18dp</dimen>
+    <dimen name="gesture_tutorial_foldable_feedback_margin_start_end">140dp</dimen>
     <dimen name="gesture_tutorial_multi_row_task_view_spacing">72dp</dimen>
     <dimen name="gesture_tutorial_small_task_view_corner_radius">18dp</dimen>
 
+    <!-- Gesture Tutorial mock conversations -->
+    <dimen name="gesture_tutorial_message_icon_size">44dp</dimen>
+    <dimen name="gesture_tutorial_message_icon_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_message_input_margin_top">36dp</dimen>
+    <dimen name="gesture_tutorial_message_large_margin_bottom">32dp</dimen>
+    <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>
+
+    <!-- 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>
+
+    <!-- Gesture Tutorial mock hotseats -->
+    <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>
+    <dimen name="gesture_tutorial_hotseat_search_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_hotseat_icon_search_margin">36dp</dimen>
+
+    <!-- Gesture Tutorial mock webpages -->
+    <dimen name="gesture_tutorial_webpage_padding_top">32dp</dimen>
+    <dimen name="gesture_tutorial_webpage_large_margin_top">24dp</dimen>
+    <dimen name="gesture_tutorial_webpage_small_margin_top">8dp</dimen>
+    <dimen name="gesture_tutorial_webpage_large_corner_radius">22dp</dimen>
+    <dimen name="gesture_tutorial_webpage_medium_corner_radius">8dp</dimen>
+    <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>
+
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">40dp</dimen>
     <dimen name="allset_title_margin_top">24dp</dimen>
@@ -165,8 +196,10 @@
     <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
     <dimen name="taskbar_folder_margin">16dp</dimen>
     <dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
-    <dimen name="taskbar_nav_buttons_size">48dp</dimen>
-    <dimen name="taskbar_contextual_button_margin">16dp</dimen>
+    <dimen name="taskbar_contextual_padding_top">8dp</dimen>
+    <dimen name="taskbar_nav_buttons_size">44dp</dimen>
+    <dimen name="taskbar_contextual_button_margin">40dp</dimen>
+    <dimen name="taskbar_hotseat_nav_spacing">42dp</dimen>
     <dimen name="taskbar_contextual_buttons_size">35dp</dimen>
     <dimen name="taskbar_stashed_size">24dp</dimen>
     <dimen name="taskbar_stashed_handle_width">220dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 52bd48b..6af0d60 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -53,8 +53,6 @@
 
     <!-- Accessibility title for the row of all-apps containing app predictions. [CHAR LIMIT=50] -->
     <string name="title_app_suggestions">App suggestions</string>
-    <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
-    <string name="all_apps_label">All apps</string>
     <!-- Text of the tip when user lands in all apps view for the first time, indicating where the tip toast points to is the predicted apps section. [CHAR_LIMIT=50] -->
     <string name="all_apps_prediction_tip">Your predicted apps</string>
 
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 088009a..216e79b 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -31,19 +31,14 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.ServiceConnection;
 import android.graphics.Insets;
 import android.hardware.SensorManager;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.Log;
 import android.view.View;
 import android.view.WindowInsets;
 import android.window.SplashScreen;
@@ -76,13 +71,13 @@
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.TouchInteractionService.TISBinder;
 import com.android.quickstep.util.LauncherUnfoldAnimationController;
 import com.android.quickstep.util.ProxyScreenStatusProvider;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.TISBindHelper;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -103,11 +98,6 @@
 public abstract class BaseQuickstepLauncher extends Launcher
         implements NavigationModeChangeListener {
 
-    private static final long BACKOFF_MILLIS = 1000;
-
-    // Max backoff caps at 5 mins
-    private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
-
     private DepthController mDepthController = new DepthController(this);
     private QuickstepTransitionManager mAppTransitionManager;
 
@@ -120,45 +110,12 @@
 
     private OverviewActionsView mActionsView;
 
+    private TISBindHelper mTISBindHelper;
     private @Nullable TaskbarManager mTaskbarManager;
     private @Nullable OverviewCommandHelper mOverviewCommandHelper;
     private @Nullable LauncherTaskbarUIController mTaskbarUIController;
-    private final ServiceConnection mTisBinderConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
-            if (!(iBinder instanceof TISBinder)) {
-                // Seems like there can be a race condition when user unlocks, which kills the TIS
-                // process and re-starts it. I guess in the meantime service can be connected to
-                // a killed TIS? Either way, unbind and try to re-connect in that case.
-                internalUnbindToTIS();
-                mHandler.postDelayed(mConnectionRunnable, BACKOFF_MILLIS);
-                return;
-            }
 
-            mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager();
-            mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
-
-            Log.d(TAG, "TIS service connected");
-            resetServiceBindRetryState();
-
-            mOverviewCommandHelper = ((TISBinder) iBinder).getOverviewCommandHelper();
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName componentName) { }
-
-        @Override
-        public void onBindingDied(ComponentName name) {
-            Log.w(TAG, "TIS binding died");
-            internalBindToTIS();
-        }
-    };
-
-    private final Runnable mConnectionRunnable = this::internalBindToTIS;
-    private short mConnectionAttempts;
     private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
-    private final Handler mHandler = new Handler();
-    private boolean mTisServiceBound;
 
     // Will be updated when dragging from taskbar.
     private @Nullable DragOptions mNextWorkspaceDragOptions = null;
@@ -201,11 +158,10 @@
 
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
 
-        internalUnbindToTIS();
+        mTISBindHelper.onDestroy();
         if (mTaskbarManager != null) {
             mTaskbarManager.clearLauncher(this);
         }
-        resetServiceBindRetryState();
 
         if (mLauncherUnfoldAnimationController != null) {
             mLauncherUnfoldAnimationController.onDestroy();
@@ -357,42 +313,13 @@
         mAppTransitionManager.registerRemoteAnimations();
         mAppTransitionManager.registerRemoteTransitions();
 
-        internalBindToTIS();
+        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
     }
 
-    /**
-     * Binds {@link #mTisBinderConnection} to {@link TouchInteractionService}. If the binding fails,
-     * attempts to retry via {@link #mConnectionRunnable}.
-     * Unbind via {@link #internalUnbindToTIS()}
-     */
-    private void internalBindToTIS() {
-        mTisServiceBound = bindService(new Intent(this, TouchInteractionService.class),
-                        mTisBinderConnection, 0);
-        if (mTisServiceBound) {
-            resetServiceBindRetryState();
-            return;
-        }
-
-        Log.w(TAG, "Retrying TIS Binder connection attempt: " + mConnectionAttempts);
-        final long timeoutMs = (long) Math.min(
-                Math.scalb(BACKOFF_MILLIS, mConnectionAttempts), MAX_BACKOFF_MILLIS);
-        mHandler.postDelayed(mConnectionRunnable, timeoutMs);
-        mConnectionAttempts++;
-    }
-
-    /** See {@link #internalBindToTIS()} */
-    private void internalUnbindToTIS() {
-        if (mTisServiceBound) {
-            unbindService(mTisBinderConnection);
-            mTisServiceBound = false;
-        }
-    }
-
-    private void resetServiceBindRetryState() {
-        if (mHandler.hasCallbacks(mConnectionRunnable)) {
-            mHandler.removeCallbacks(mConnectionRunnable);
-        }
-        mConnectionAttempts = 0;
+    private void onTISConnected(TISBinder binder) {
+        mTaskbarManager = binder.getTaskbarManager();
+        mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
+        mOverviewCommandHelper = binder.getOverviewCommandHelper();
     }
 
     private void initUnfoldTransitionProgressProvider() {
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 63e7390..680012c 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -30,6 +30,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.FolderInfo;
@@ -298,7 +299,7 @@
             Log.e(TAG, "Unable to find suitable view for ArrowTip");
             return false;
         }
-        Rect bounds = mLauncher.getViewBounds(tipTargetView);
+        Rect bounds = Utilities.getViewBounds(tipTargetView);
         new ArrowTipView(mLauncher).show(message, Gravity.END, bounds.centerX(), bounds.top);
         return true;
     }
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 154b78b..e489cb3 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -377,7 +377,7 @@
     /**
      * Shortcut factory for generating wellbeing action
      */
-    public static final SystemShortcut.Factory SHORTCUT_FACTORY =
+    public static final SystemShortcut.Factory<BaseDraggingActivity> SHORTCUT_FACTORY =
             (activity, info) -> (info.getTargetComponent() == null) ? null : INSTANCE.get(activity)
                     .getShortcutForApp(
                             info.getTargetComponent().getPackageName(), info.user.getIdentifier(),
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 6bc49ed..71a93d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -16,8 +16,10 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
+import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
 
 import android.animation.Animator;
@@ -37,6 +39,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
@@ -54,6 +57,7 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.Arrays;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 /**
@@ -62,7 +66,6 @@
 public class LauncherTaskbarUIController extends TaskbarUIController {
 
     private final BaseQuickstepLauncher mLauncher;
-    private final TaskbarStateHandler mTaskbarStateHandler;
 
     private final TaskbarActivityContext mContext;
     private final TaskbarDragLayer mTaskbarDragLayer;
@@ -72,17 +75,64 @@
             new AnimatedFloat(this::onIconAlignmentRatioChanged);
     private final AnimatedFloat mIconAlignmentForGestureState =
             new AnimatedFloat(this::onIconAlignmentRatioChanged);
+    private final AnimatedFloat mIconAlignmentForLauncherState =
+            new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
 
     private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
             this::onStashedInAppChanged;
 
     private final StateManager.StateListener<LauncherState> mStateListener =
             new StateManager.StateListener<LauncherState>() {
+                private Animator mAnimator;
+
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                    // Stash animation from going to launcher should be already handled in
+                    // createAnimToLauncher.
+                    TaskbarStashController controller = mControllers.taskbarStashController;
+                    long duration = TASKBAR_STASH_DURATION;
+                    controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                            toState.isTaskbarStashed());
+                    Animator stashAnimator = controller.applyStateWithoutStart(duration);
+                    if (stashAnimator != null) {
+                        if (mAnimator != null) {
+                            mAnimator.cancel();
+                        }
+                        PendingAnimation pendingAnimation = new PendingAnimation(duration);
+                        pendingAnimation.add(stashAnimator);
+                        pendingAnimation.setFloat(mIconAlignmentForLauncherState,
+                                AnimatedFloat.VALUE, toState.isTaskbarStashed() ? 0 : 1,
+                                FAST_OUT_SLOW_IN);
+                        pendingAnimation.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationStart(Animator animator) {
+                                mTargetStateOverrideForStateTransition = toState;
+                                // Copy hotseat alpha over to taskbar icons
+                                mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
+                                mLauncher.getHotseat().setIconsAlpha(0);
+                            }
+
+                            @Override
+                            public void onAnimationEnd(Animator animator) {
+                                if (toState.isTaskbarStashed()) {
+                                    // Reset hotseat alpha to default
+                                    mLauncher.getHotseat().setIconsAlpha(1);
+                                }
+                                mTargetStateOverrideForStateTransition = null;
+                                mAnimator = null;
+                            }
+                        });
+                        mAnimator = pendingAnimation.buildAnim();
+                        mAnimator.start();
+                    }
+                }
+
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
                     TaskbarStashController controller = mControllers.taskbarStashController;
                     controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
                             finalState.isTaskbarStashed());
+                    controller.applyState();
                 }
             };
 
@@ -96,6 +146,16 @@
     private TaskbarKeyguardController mKeyguardController;
 
     private LauncherState mTargetStateOverride = null;
+    private LauncherState mTargetStateOverrideForStateTransition = null;
+
+    private final DeviceProfile.OnDeviceProfileChangeListener mProfileChangeListener =
+            new DeviceProfile.OnDeviceProfileChangeListener() {
+                @Override
+                public void onDeviceProfileChanged(DeviceProfile dp) {
+                    mControllers.taskbarViewController.onRotationChanged(
+                            mLauncher.getDeviceProfile());
+                }
+            };
 
     public LauncherTaskbarUIController(
             BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
@@ -104,7 +164,6 @@
         mTaskbarView = mTaskbarDragLayer.findViewById(R.id.taskbar_view);
 
         mLauncher = launcher;
-        mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
     }
 
     @Override
@@ -122,13 +181,14 @@
         mLauncher.setTaskbarUIController(this);
         mKeyguardController = taskbarControllers.taskbarKeyguardController;
 
-        onLauncherResumedOrPaused(mLauncher.hasBeenResumed());
+        onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
         mIconAlignmentForResumedState.finishAnimation();
         onIconAlignmentRatioChanged();
 
         onStashedInAppChanged(mLauncher.getDeviceProfile());
         mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
         mLauncher.getStateManager().addStateListener(mStateListener);
+        mLauncher.addOnDeviceProfileChangeListener(mProfileChangeListener);
     }
 
     @Override
@@ -136,11 +196,13 @@
         onLauncherResumedOrPaused(false);
         mIconAlignmentForResumedState.finishAnimation();
         mIconAlignmentForGestureState.finishAnimation();
+        mIconAlignmentForLauncherState.finishAnimation();
 
         mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
         mLauncher.getStateManager().removeStateListener(mStateListener);
         mLauncher.getHotseat().setIconsAlpha(1f);
         mLauncher.setTaskbarUIController(null);
+        mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener);
     }
 
     @Override
@@ -162,6 +224,10 @@
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
     public void onLauncherResumedOrPaused(boolean isResumed) {
+        onLauncherResumedOrPaused(isResumed, false /* fromInit */);
+    }
+
+    private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) {
         if (mKeyguardController.isScreenOff()) {
             if (!isResumed) {
                 return;
@@ -172,6 +238,11 @@
         }
 
         long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
+        if (fromInit) {
+            // Since we are creating the starting state, we don't have a state to animate from, so
+            // set our state immediately.
+            duration = 0;
+        }
         ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
                 getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
                 .setDuration(duration);
@@ -236,25 +307,35 @@
         return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
     }
 
+    private float getCurrentIconAlignmentRatioForLauncherState() {
+        return mIconAlignmentForLauncherState.value;
+    }
+
+    private void onIconAlignmentRatioChangedForStateTransition() {
+        onIconAlignmentRatioChanged(
+                mTargetStateOverrideForStateTransition != null
+                        ? mTargetStateOverrideForStateTransition
+                        : mLauncher.getStateManager().getState(),
+                this::getCurrentIconAlignmentRatioForLauncherState);
+    }
+
     private void onIconAlignmentRatioChanged() {
+        onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
+                : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
+    }
+
+    private void onIconAlignmentRatioChanged(LauncherState state,
+            Supplier<Float> alignmentSupplier) {
         if (mControllers == null) {
             return;
         }
-        float alignment = getCurrentIconAlignmentRatio();
+        float alignment = alignmentSupplier.get();
         mControllers.taskbarViewController.setLauncherIconAlignment(
                 alignment, mLauncher.getDeviceProfile());
 
         mTaskbarBackgroundAlpha.updateValue(1 - alignment);
 
-        LauncherState state = mTargetStateOverride != null ? mTargetStateOverride
-                : mLauncher.getStateManager().getState();
-        if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
-            // If the hotseat icons are visible, then switch taskbar in last frame
-            setTaskbarViewVisible(alignment < 1);
-        } else {
-            mLauncher.getHotseat().setIconsAlpha(1);
-            mIconAlphaForHome.setValue(1 - alignment);
-        }
+        setIconAlpha(state, alignment);
     }
 
     /**
@@ -273,6 +354,15 @@
         return mTaskbarDragLayer;
     }
 
+    private void setIconAlpha(LauncherState state, float progress) {
+        if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+            // If the hotseat icons are visible, then switch taskbar in last frame
+            setTaskbarViewVisible(progress < 1);
+        } else {
+            mIconAlphaForHome.setValue(1 - progress);
+        }
+    }
+
     private void setTaskbarViewVisible(boolean isVisible) {
         mIconAlphaForHome.setValue(isVisible ? 1 : 0);
         mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 4b75db4..b768d60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -36,12 +36,13 @@
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
+import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import android.provider.Settings;
 import android.util.Property;
+import android.view.Gravity;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnHoverListener;
@@ -57,7 +58,7 @@
 import com.android.launcher3.taskbar.contextual.RotationButton;
 import com.android.launcher3.taskbar.contextual.RotationButtonController;
 import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.Themes;
 import com.android.quickstep.AnimatedFloat;
 
 import java.util.ArrayList;
@@ -95,6 +96,9 @@
     private final ViewGroup mEndContextualContainer;
     private final ViewGroup mStartContextualContainer;
 
+    private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
+            this::updateNavButtonTranslationY);
+
     // Initialized in init.
     private TaskbarControllers mControllers;
     private View mA11yButton;
@@ -112,9 +116,10 @@
     /**
      * Initializes the controller
      */
-    public void init(TaskbarControllers controllers) {
+    public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
         mControllers = controllers;
         mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
+        parseSystemUiFlags(sharedState.sysuiStateFlags);
 
         mA11yLongClickListener = view -> {
             mControllers.navButtonController.onButtonClick(BUTTON_A11Y_LONG_CLICK);
@@ -133,8 +138,7 @@
                 mControllers.navButtonController, R.id.ime_switcher);
         mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
                 flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
-                        && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)
-                        && ((flags & FLAG_A11Y_VISIBLE) == 0)));
+                        && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
 
         mPropertyHolders.add(new StatePropertyHolder(
                 mControllers.taskbarViewController.getTaskbarIconAlpha()
@@ -146,12 +150,32 @@
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, AnimatedFloat.VALUE, 1, 0));
 
         // Force nav buttons (specifically back button) to be visible during setup wizard.
-        boolean areButtonsForcedVisible = !SettingsCache.INSTANCE.get(mContext).getValue(
-                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
-        if (isThreeButtonNav || areButtonsForcedVisible) {
+        boolean isInSetup = !mContext.isUserSetupComplete();
+        if (isThreeButtonNav || isInSetup) {
             initButtons(mNavButtonContainer, mEndContextualContainer,
                     mControllers.navButtonController);
 
+            if (isInSetup) {
+                // Since setup wizard only has back button enabled, it looks strange to be
+                // end-aligned, so start-align instead.
+                FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+                        mNavButtonContainer.getLayoutParams();
+                navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd());
+                navButtonsLayoutParams.setMarginEnd(0);
+                navButtonsLayoutParams.gravity = Gravity.START;
+                mNavButtonContainer.requestLayout();
+
+                if (!isThreeButtonNav) {
+                    // Tint all the nav buttons since there's no taskbar background in SUW.
+                    for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+                        if (!(mNavButtonContainer.getChildAt(i) instanceof ImageView)) continue;
+                        ImageView button = (ImageView) mNavButtonContainer.getChildAt(i);
+                        button.setImageTintList(ColorStateList.valueOf(Themes.getAttrColor(
+                                button.getContext(), android.R.attr.textColorPrimary)));
+                    }
+                }
+            }
+
             // Animate taskbar background when IME shows
             mPropertyHolders.add(new StatePropertyHolder(
                     mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
@@ -226,23 +250,14 @@
         mA11yButton.setOnLongClickListener(mA11yLongClickListener);
     }
 
-    public void updateStateForSysuiFlags(int systemUiStateFlags, boolean forceUpdate) {
-        boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
-        boolean isImeSwitcherShowing = (systemUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0;
-        boolean a11yVisible = (systemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
-        boolean a11yLongClickable =
-                (systemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
-        boolean isHomeDisabled =
-                (systemUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
-        boolean isRecentsDisabled =
-                (systemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
-        boolean isBackDisabled =
-                (systemUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0;
-
-        if (!forceUpdate && systemUiStateFlags == mSysuiStateFlags) {
-            return;
-        }
-        mSysuiStateFlags = systemUiStateFlags;
+    private void parseSystemUiFlags(int sysUiStateFlags) {
+        mSysuiStateFlags = sysUiStateFlags;
+        boolean isImeVisible = (sysUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+        boolean isImeSwitcherShowing = (sysUiStateFlags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0;
+        boolean a11yVisible = (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+        boolean isHomeDisabled = (sysUiStateFlags & SYSUI_STATE_HOME_DISABLED) != 0;
+        boolean isRecentsDisabled = (sysUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
+        boolean isBackDisabled = (sysUiStateFlags & SYSUI_STATE_BACK_DISABLED) != 0;
 
         // TODO(b/202218289) we're getting IME as not visible on lockscreen from system
         updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
@@ -254,8 +269,17 @@
 
         if (mA11yButton != null) {
             // Only used in 3 button
+            boolean a11yLongClickable =
+                    (sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
             mA11yButton.setLongClickable(a11yLongClickable);
         }
+    }
+
+    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        if (systemUiStateFlags == mSysuiStateFlags) {
+            return;
+        }
+        parseSystemUiFlags(systemUiStateFlags);
         applyState();
     }
 
@@ -285,6 +309,13 @@
     }
 
     /**
+     * Returns true if the home button is disabled
+     */
+    public boolean isHomeDisabled() {
+        return (mState & FLAG_DISABLE_HOME) != 0;
+    }
+
+    /**
      * Returns true if the recents (overview) button is disabled
      */
     public boolean isRecentsDisabled() {
@@ -305,6 +336,11 @@
         }
     }
 
+    /** Use to set the translationY for the all nav+contextual buttons */
+    public AnimatedFloat getTaskbarNavButtonTranslationY() {
+        return mTaskbarNavButtonTranslationY;
+    }
+
     /**
      * Does not call {@link #applyState()}. Don't forget to!
      */
@@ -323,6 +359,10 @@
         }
     }
 
+    private void updateNavButtonTranslationY() {
+        mNavButtonsView.setTranslationY(mTaskbarNavButtonTranslationY.value);
+    }
+
     private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
             ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id) {
         return addButton(drawableId, buttonType, parent, navButtonController, id,
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 10da826..2c80f06 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -30,6 +30,7 @@
 import com.android.launcher3.anim.RevealOutlineAnimation;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 
@@ -38,6 +39,10 @@
  */
 public class StashedHandleViewController {
 
+    public static final int ALPHA_INDEX_STASHED = 0;
+    public static final int ALPHA_INDEX_HOME_DISABLED = 1;
+    private static final int NUM_ALPHA_CHANNELS = 2;
+
     /**
      * The SharedPreferences key for whether the stashed handle region is dark.
      */
@@ -50,8 +55,7 @@
     private final int mStashedHandleWidth;
     private final int mStashedHandleHeight;
     private final RegionSamplingHelper mRegionSamplingHelper;
-    private final AnimatedFloat mTaskbarStashedHandleAlpha = new AnimatedFloat(
-            this::updateStashedHandleAlpha);
+    private final MultiValueAlpha mTaskbarStashedHandleAlpha;
     private final AnimatedFloat mTaskbarStashedHandleHintScale = new AnimatedFloat(
             this::updateStashedHandleHintScale);
 
@@ -69,6 +73,8 @@
         mActivity = activity;
         mPrefs = Utilities.getPrefs(mActivity);
         mStashedHandleView = stashedHandleView;
+        mTaskbarStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, NUM_ALPHA_CHANNELS);
+        mTaskbarStashedHandleAlpha.setUpdateVisibility(true);
         mStashedHandleView.updateHandleColor(
                 mPrefs.getBoolean(SHARED_PREFS_STASHED_HANDLE_REGION_DARK_KEY, false),
                 false /* animate */);
@@ -96,7 +102,7 @@
         mControllers = controllers;
         mStashedHandleView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
 
-        updateStashedHandleAlpha();
+        mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(0);
         mTaskbarStashedHandleHintScale.updateValue(1f);
 
         final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
@@ -129,7 +135,7 @@
         mRegionSamplingHelper.stopAndDestroy();
     }
 
-    public AnimatedFloat getStashedHandleAlpha() {
+    public MultiValueAlpha getStashedHandleAlpha() {
         return mTaskbarStashedHandleAlpha;
     }
 
@@ -163,12 +169,20 @@
         }
     }
 
-    protected void updateStashedHandleAlpha() {
-        mStashedHandleView.setAlpha(mTaskbarStashedHandleAlpha.value);
-    }
-
     protected void updateStashedHandleHintScale() {
         mStashedHandleView.setScaleX(mTaskbarStashedHandleHintScale.value);
         mStashedHandleView.setScaleY(mTaskbarStashedHandleHintScale.value);
     }
+
+    /**
+     * Should be called when the home button is disabled, so we can hide this handle as well.
+     */
+    public void setIsHomeButtonDisabled(boolean homeDisabled) {
+        mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_HOME_DISABLED).setValue(
+                homeDisabled ? 0 : 1);
+    }
+
+    public boolean isStashedHandleVisible() {
+        return mStashedHandleView.getVisibility() == View.VISIBLE;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index bda5038..370496a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -35,6 +35,7 @@
 import android.graphics.Rect;
 import android.os.Process;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
@@ -61,6 +62,7 @@
 import com.android.launcher3.taskbar.contextual.RotationButtonController;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.ViewCache;
@@ -102,6 +104,7 @@
     private final ViewCache mViewCache = new ViewCache();
 
     private final boolean mIsSafeModeEnabled;
+    private final boolean mIsUserSetupComplete;
     private boolean mIsDestroyed = false;
 
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
@@ -113,6 +116,8 @@
         mNavMode = SysUINavigationMode.getMode(windowContext);
         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
                 () -> getPackageManager().isSafeMode());
+        mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
 
         float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
         mDeviceProfile.updateIconSize(1, getResources());
@@ -125,6 +130,7 @@
         mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
                 R.layout.taskbar, null, false);
         TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
+        TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
         FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
         StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
 
@@ -145,6 +151,7 @@
                         R.color.popup_color_primary_light),
                 new TaskbarDragLayerController(this, mDragLayer),
                 new TaskbarViewController(this, taskbarView),
+                new TaskbarScrimViewController(this, taskbarScrimView),
                 new TaskbarUnfoldAnimationController(unfoldTransitionProgressProvider,
                         mWindowManager),
                 new TaskbarKeyguardController(this),
@@ -153,7 +160,7 @@
                 new TaskbarEduController(this));
     }
 
-    public void init() {
+    public void init(TaskbarSharedState sharedState) {
         mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
         mWindowLayoutParams = new WindowManager.LayoutParams(
                 MATCH_PARENT,
@@ -182,7 +189,7 @@
                 getDefaultTaskbarWindowHeight() - mDeviceProfile.taskbarSize, 0, 0);
 
         // Initialize controllers after all are constructed.
-        mControllers.init();
+        mControllers.init(sharedState);
 
         mWindowManager.addView(mDragLayer, mWindowLayoutParams);
     }
@@ -301,6 +308,13 @@
     }
 
     /**
+     * Sets the flag indicating setup UI is visible
+     */
+    public void setSetupUIVisible(boolean isVisible) {
+        mControllers.taskbarStashController.setSetupUIVisible(isVisible);
+    }
+
+    /**
      * Called when this instance of taskbar is no longer needed
      */
     public void onDestroy() {
@@ -310,9 +324,8 @@
         mWindowManager.removeViewImmediate(mDragLayer);
     }
 
-    public void updateSysuiStateFlags(int systemUiStateFlags, boolean forceUpdate) {
-        mControllers.navbarButtonsViewController.updateStateForSysuiFlags(
-                systemUiStateFlags, forceUpdate);
+    public void updateSysuiStateFlags(int systemUiStateFlags) {
+        mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags);
         mControllers.taskbarViewController.setImeIsVisible(
                 mControllers.navbarButtonsViewController.isImeVisible());
         boolean panelExpanded = (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
@@ -321,8 +334,11 @@
                 panelExpanded || inSettings);
         mControllers.taskbarViewController.setRecentsButtonDisabled(
                 mControllers.navbarButtonsViewController.isRecentsDisabled());
+        mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
+                mControllers.navbarButtonsViewController.isHomeDisabled());
         mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
         mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags);
     }
 
     public void onRotationProposal(int rotation, boolean isValid) {
@@ -462,4 +478,8 @@
     public void startTaskbarUnstashHint(boolean animateForward) {
         mControllers.taskbarStashController.startUnstashHint(animateForward);
     }
+
+    protected boolean isUserSetupComplete() {
+        return mIsUserSetupComplete;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index e49c6ae..8684c29 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -30,6 +30,7 @@
     public final NavbarButtonsViewController navbarButtonsViewController;
     public final RotationButtonController rotationButtonController;
     public final TaskbarDragLayerController taskbarDragLayerController;
+    public final TaskbarScrimViewController taskbarScrimViewController;
     public final TaskbarViewController taskbarViewController;
     public final TaskbarUnfoldAnimationController taskbarUnfoldAnimationController;
     public final TaskbarKeyguardController taskbarKeyguardController;
@@ -47,6 +48,7 @@
             RotationButtonController rotationButtonController,
             TaskbarDragLayerController taskbarDragLayerController,
             TaskbarViewController taskbarViewController,
+            TaskbarScrimViewController taskbarScrimViewController,
             TaskbarUnfoldAnimationController taskbarUnfoldAnimationController,
             TaskbarKeyguardController taskbarKeyguardController,
             StashedHandleViewController stashedHandleViewController,
@@ -59,6 +61,7 @@
         this.rotationButtonController = rotationButtonController;
         this.taskbarDragLayerController = taskbarDragLayerController;
         this.taskbarViewController = taskbarViewController;
+        this.taskbarScrimViewController = taskbarScrimViewController;
         this.taskbarUnfoldAnimationController = taskbarUnfoldAnimationController;
         this.taskbarKeyguardController = taskbarKeyguardController;
         this.stashedHandleViewController = stashedHandleViewController;
@@ -71,17 +74,18 @@
      * TaskbarControllers instance, but should be careful to only access things that were created
      * in constructors for now, as some controllers may still be waiting for init().
      */
-    public void init() {
-        navbarButtonsViewController.init(this);
+    public void init(TaskbarSharedState sharedState) {
+        navbarButtonsViewController.init(this, sharedState);
         if (taskbarActivityContext.isThreeButtonNav()) {
             rotationButtonController.init();
         }
         taskbarDragLayerController.init(this);
         taskbarViewController.init(this);
+        taskbarScrimViewController.init(this);
         taskbarUnfoldAnimationController.init(this);
         taskbarKeyguardController.init(navbarButtonsViewController);
         stashedHandleViewController.init(this);
-        taskbarStashController.init(this);
+        taskbarStashController.init(this, sharedState);
         taskbarEduController.init(this);
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index dc7199e..5bedf87 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -76,7 +76,7 @@
      * Cache a copy here so we can initialize state whenever taskbar is recreated, since
      * this class does not get re-initialized w/ new taskbars.
      */
-    private int mSysuiStateFlags;
+    private final TaskbarSharedState mSharedState = new TaskbarSharedState();
 
     private static final int CHANGE_FLAGS =
             CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
@@ -192,22 +192,27 @@
 
         mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp.copy(mContext),
                 mNavButtonController, mUnfoldProgressProvider);
-        mTaskbarActivityContext.init();
+        mTaskbarActivityContext.init(mSharedState);
         if (mLauncher != null) {
             mTaskbarActivityContext.setUIController(
                     new LauncherTaskbarUIController(mLauncher, mTaskbarActivityContext));
         }
-        onSysuiFlagsChangedInternal(mSysuiStateFlags, true /* forceUpdate */);
     }
 
     public void onSystemUiFlagsChanged(int systemUiStateFlags) {
-        onSysuiFlagsChangedInternal(systemUiStateFlags, false /* forceUpdate */);
+        mSharedState.sysuiStateFlags = systemUiStateFlags;
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.updateSysuiStateFlags(systemUiStateFlags);
+        }
     }
 
-    private void onSysuiFlagsChangedInternal(int systemUiStateFlags, boolean forceUpdate) {
-        mSysuiStateFlags = systemUiStateFlags;
+    /**
+     * Sets the flag indicating setup UI is visible
+     */
+    public void setSetupUIVisible(boolean isVisible) {
+        mSharedState.setupUIVisible = isVisible;
         if (mTaskbarActivityContext != null) {
-            mTaskbarActivityContext.updateSysuiStateFlags(systemUiStateFlags, forceUpdate);
+            mTaskbarActivityContext.setSetupUIVisible(isVisible);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
new file mode 100644
index 0000000..94a3307
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -0,0 +1,129 @@
+/*
+ * 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.taskbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * View that handles scrimming the taskbar and the inverted corners it draws. The scrim is used
+ * when bubbles is expanded.
+ */
+public class TaskbarScrimView extends View {
+    private final Paint mTaskbarScrimPaint;
+    private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
+
+    private boolean mShowScrim;
+    private float mLeftCornerRadius, mRightCornerRadius;
+    private float mBackgroundHeight;
+
+    public TaskbarScrimView(Context context) {
+        this(context, null);
+    }
+
+    public TaskbarScrimView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskbarScrimView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskbarScrimView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mTaskbarScrimPaint = new Paint();
+        mTaskbarScrimPaint.setColor(getResources().getColor(android.R.color.system_neutral1_1000));
+        mTaskbarScrimPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+        mTaskbarScrimPaint.setStyle(Paint.Style.FILL);
+
+        mInvertedLeftCornerPath = new Path();
+        mInvertedRightCornerPath = new Path();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mShowScrim) {
+            canvas.save();
+            canvas.translate(0, canvas.getHeight() - mBackgroundHeight);
+
+            // Scrim the taskbar itself.
+            canvas.drawRect(0, 0, canvas.getWidth(), mBackgroundHeight, mTaskbarScrimPaint);
+
+            // Scrim the inverted rounded corners above the taskbar.
+            canvas.translate(0, -mLeftCornerRadius);
+            canvas.drawPath(mInvertedLeftCornerPath, mTaskbarScrimPaint);
+            canvas.translate(0, mLeftCornerRadius);
+            canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
+            canvas.drawPath(mInvertedRightCornerPath, mTaskbarScrimPaint);
+
+            canvas.restore();
+        }
+    }
+
+    /**
+     * Sets the height of the taskbar background.
+     * @param height the height of the background.
+     */
+    protected void setBackgroundHeight(float height) {
+        mBackgroundHeight = height;
+        if (mShowScrim) {
+            invalidate();
+        }
+    }
+
+    /**
+     * Sets the alpha of the taskbar scrim.
+     * @param alpha the alpha of the scrim.
+     */
+    protected void setScrimAlpha(float alpha) {
+        mShowScrim = alpha > 0f;
+        mTaskbarScrimPaint.setAlpha((int) (alpha * 255));
+        invalidate();
+    }
+
+    /**
+     * Sets the radius of the left and right corners above the taskbar.
+     * @param leftCornerRadius the radius of the left corner.
+     * @param rightCornerRadius the radius of the right corner.
+     */
+    protected void setCornerSizes(float leftCornerRadius, float rightCornerRadius) {
+        mLeftCornerRadius = leftCornerRadius;
+        mRightCornerRadius = rightCornerRadius;
+
+        Path square = new Path();
+        square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
+        Path circle = new Path();
+        circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
+        mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+        square.reset();
+        square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
+        circle.reset();
+        circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
+        mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
+
+        if (mShowScrim) {
+            invalidate();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
new file mode 100644
index 0000000..e7e55ef
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.taskbar;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
+
+import android.animation.ObjectAnimator;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+
+/**
+ * Handles properties/data collection, and passes the results to {@link TaskbarScrimView} to render.
+ */
+public class TaskbarScrimViewController {
+
+    private static final float SCRIM_ALPHA = 0.6f;
+
+    private static final Interpolator SCRIM_ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+    private static final Interpolator SCRIM_ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+
+    private final TaskbarActivityContext mActivity;
+    private final TaskbarScrimView mScrimView;
+
+    // Alpha property for the scrim.
+    private final AnimatedFloat mScrimAlpha = new AnimatedFloat(this::updateScrimAlpha);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
+    public TaskbarScrimViewController(TaskbarActivityContext activity, TaskbarScrimView scrimView) {
+        mActivity = activity;
+        mScrimView = scrimView;
+        mScrimView.setCornerSizes(mActivity.getLeftCornerRadius(),
+                mActivity.getRightCornerRadius());
+        mScrimView.setBackgroundHeight(mActivity.getDeviceProfile().taskbarSize);
+    }
+
+    /**
+     * Initializes the controller
+     */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    /**
+     * Updates the scrim state based on the flags.
+     */
+    public void updateStateForSysuiFlags(int stateFlags) {
+        final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+        final boolean manageMenuExpanded =
+                (stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
+        final boolean showScrim = !mControllers.navbarButtonsViewController.isImeVisible()
+                && bubblesExpanded && mControllers.taskbarStashController.isInAppAndNotStashed();
+        final float scrimAlpha = manageMenuExpanded
+                // When manage menu shows there's the first scrim and second scrim so figure out
+                // what the total transparency would be.
+                ? (SCRIM_ALPHA + (SCRIM_ALPHA * (1 - SCRIM_ALPHA)))
+                : showScrim ? SCRIM_ALPHA : 0;
+        showScrim(showScrim, scrimAlpha);
+    }
+
+    private void showScrim(boolean showScrim, float alpha) {
+        mScrimView.setOnClickListener(showScrim ? (view) -> onClick() : null);
+        mScrimView.setClickable(showScrim);
+        ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
+        anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
+        anim.start();
+    }
+
+    private void updateScrimAlpha() {
+        mScrimView.setScrimAlpha(mScrimAlpha.value);
+    }
+
+    private void onClick() {
+        SystemUiProxy.INSTANCE.get(mActivity).onBackPressed();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
new file mode 100644
index 0000000..23beef0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -0,0 +1,27 @@
+/*
+ * 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.taskbar;
+
+/**
+ * State shared across different taskbar instance
+ */
+public class TaskbarSharedState {
+
+    public int sysuiStateFlags;
+
+    public boolean setupUIVisible = false;
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 6d6f0f2..0dd4ef1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -47,16 +47,17 @@
     public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
     public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
     public static final int FLAG_STASHED_IN_APP_EMPTY = 1 << 3; // no hotseat icons
-    public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 4;
+    public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 4; // setup wizard and AllSetActivity
+    public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 5;
 
     // If we're in an app and any of these flags are enabled, taskbar should be stashed.
     public static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
-            | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY;
+            | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP;
 
     /**
      * How long to stash/unstash when manually invoked via long press.
      */
-    private static final long TASKBAR_STASH_DURATION = 300;
+    public static final long TASKBAR_STASH_DURATION = 300;
 
     /**
      * The scale TaskbarView animates to when being stashed.
@@ -103,7 +104,7 @@
     private AnimatedFloat mIconScaleForStash;
     private AnimatedFloat mIconTranslationYForStash;
     // Stashed handle properties.
-    private AnimatedFloat mTaskbarStashedHandleAlpha;
+    private AlphaProperty mTaskbarStashedHandleAlpha;
     private AnimatedFloat mTaskbarStashedHandleHintScale;
 
     /** Whether we are currently visually stashed (might change based on launcher state). */
@@ -129,7 +130,7 @@
         mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
     }
 
-    public void init(TaskbarControllers controllers) {
+    public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
         mControllers = controllers;
 
         TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController;
@@ -143,12 +144,15 @@
 
         StashedHandleViewController stashedHandleController =
                 controllers.stashedHandleViewController;
-        mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha();
+        mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha().getProperty(
+                StashedHandleViewController.ALPHA_INDEX_STASHED);
         mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
 
         boolean isManuallyStashedInApp = supportsManualStashing()
                 && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
         updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP,
+                !mActivity.isUserSetupComplete() || sharedState.setupUIVisible);
         applyState();
 
         SystemUiProxy.INSTANCE.get(mActivity)
@@ -177,6 +181,15 @@
     }
 
     /**
+     * Sets the flag indicating setup UI is visible
+     */
+    protected void setSetupUIVisible(boolean isVisible) {
+        updateStateForFlag(FLAG_STASHED_IN_APP_SETUP,
+                isVisible || !mActivity.isUserSetupComplete());
+        applyState();
+    }
+
+    /**
      * Returns whether the taskbar is currently visually stashed.
      */
     public boolean isStashed() {
@@ -198,8 +211,21 @@
         return (flags & flagMask) != 0;
     }
 
+
+    /**
+     * Returns whether the taskbar is currently visible and in an app.
+     */
+    public boolean isInAppAndNotStashed() {
+        return !mIsStashed && (mState & FLAG_IN_APP) != 0;
+    }
+
     public int getContentHeight() {
-        return isStashed() ? mStashedHeight : mUnstashedHeight;
+        if (isStashed()) {
+            boolean isAnimating = mAnimator != null && mAnimator.isStarted();
+            return mControllers.stashedHandleViewController.isStashedHandleVisible() || isAnimating
+                    ? mStashedHeight : 0;
+        }
+        return mUnstashedHeight;
     }
 
     public int getStashedHeight() {
@@ -240,7 +266,12 @@
         return false;
     }
 
-    private Animator createAnimToIsStashed(boolean isStashed, long duration) {
+    /**
+     * Create a stash animation and save to {@link #mAnimator}.
+     * @param isStashed whether it's a stash animation or an unstash animation
+     * @param duration duration of the animation
+     */
+    private void createAnimToIsStashed(boolean isStashed, long duration) {
         if (mAnimator != null) {
             mAnimator.cancel();
         }
@@ -250,7 +281,7 @@
             // Just hide/show the icons instead of stashing into a handle.
             mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
                     .setDuration(duration));
-            return mAnimator;
+            return;
         }
 
         AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
@@ -322,7 +353,6 @@
                 mAnimator = null;
             }
         });
-        return mAnimator;
     }
 
     /**
@@ -442,12 +472,13 @@
             boolean isStashed = mStashCondition.test(flags);
             if (mIsStashed != isStashed) {
                 mIsStashed = isStashed;
-                Animator animator = createAnimToIsStashed(mIsStashed, duration);
+                createAnimToIsStashed(mIsStashed, duration);
                 if (start) {
-                    animator.start();
+                    mAnimator.start();
                 }
+                return mAnimator;
             }
-            return mAnimator;
+            return null;
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index ed09fa5..d14622b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -60,6 +60,7 @@
             this::updateTranslationY);
     private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
             this::updateTranslationY);
+    private AnimatedFloat mTaskbarNavButtonTranslationY;
 
     private final TaskbarModelCallbacks mModelCallbacks;
 
@@ -87,7 +88,12 @@
         mTaskbarIconScaleForStash.updateValue(1f);
 
         mModelCallbacks.init(controllers);
-        LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
+        if (mActivity.isUserSetupComplete()) {
+            // Only load the callbacks if user setup is completed
+            LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
+        }
+        mTaskbarNavButtonTranslationY =
+                controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
     }
 
     public void onDestroy() {
@@ -214,6 +220,7 @@
 
         int offsetY = launcherDp.getTaskbarOffsetY();
         setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
+        setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
 
         int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
         int expandedHeight = Math.max(collapsedHeight,
@@ -238,6 +245,14 @@
         return controller;
     }
 
+    public void onRotationChanged(DeviceProfile deviceProfile) {
+        if (areIconsVisible()) {
+            // We only translate on rotation when on home
+            return;
+        }
+        mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
+    }
+
     /**
      * Callbacks for {@link TaskbarView} to interact with its controller.
      */
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index c9909cc..aa26645 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -57,13 +57,13 @@
         if (SysUINavigationMode.INSTANCE.get(context).getMode() == Mode.THREE_BUTTONS) {
             Resources res = context.getResources();
             /*
-            * 2 (left + right) x Padding +
             * 3 nav buttons +
-            * Little space at the end for contextual buttons
+            * Little space at the end for contextual buttons +
+            * Little space between icons and nav buttons
             */
-            return 2 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_spacing)
-                    + 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
-                    + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin);
+            return 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+                    + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin)
+                    + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
         } else {
             return 0;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index f8c9fd1..c554fd0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -69,6 +69,11 @@
     }
 
     @Override
+    public boolean isTaskbarStashed() {
+        return true;
+    }
+
+    @Override
     protected float getDepthUnchecked(Context context) {
         // The scrim fades in at approximately 50% of the swipe gesture.
         // This means that the depth should be greater than 1, in order to fully zoom out.
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index aabba66..99f1dc7 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -122,10 +122,6 @@
     public static final int STATE_RECENTS_ANIMATION_ENDED =
             getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
 
-    // Called when we create an overscroll window when swiping right to left on the most recent app
-    public static final int STATE_OVERSCROLL_WINDOW_CREATED =
-            getFlagForIndex("STATE_OVERSCROLL_WINDOW_CREATED");
-
     // Called when RecentsView stops scrolling and settles on a TaskView.
     public static final int STATE_RECENTS_SCROLLING_FINISHED =
             getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
diff --git a/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java b/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
deleted file mode 100644
index 4c261ab..0000000
--- a/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.systemui.plugins.OverscrollPlugin;
-
-/**
- * Resource overrideable factory for forcing a local overscroll plugin.
- * Override {@link R.string#overscroll_plugin_factory_class} to set a different class.
- */
-public class OverscrollPluginFactory implements ResourceBasedOverride {
-    public static final MainThreadInitializedObject<OverscrollPluginFactory> INSTANCE = forOverride(
-            OverscrollPluginFactory.class,
-            R.string.overscroll_plugin_factory_class);
-
-    /**
-     * Get the plugin that is defined locally in launcher, as opposed to a dynamic side loaded one.
-     */
-    public OverscrollPlugin getLocalOverscrollPlugin() {
-        return null;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 6ccb152..95ab62f 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -63,6 +63,17 @@
                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, focusedTaskRect.height());
                 return response;
             }
+
+            case TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET: {
+                if (!mDeviceProfile.isTablet) {
+                    return null;
+                }
+                Rect gridTaskRect = new Rect();
+                LauncherActivityInterface.INSTANCE.calculateGridTaskSize(mContext, mDeviceProfile,
+                        gridTaskRect, PagedOrientationHandler.PORTRAIT);
+                response.putParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD, gridTaskRect);
+                return response;
+            }
         }
 
         return super.call(method, arg);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 8a9bf7c..e2441ed 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -84,7 +84,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * Manages the state of the system during a swipe up gesture.
@@ -398,14 +397,6 @@
     }
 
     /**
-     * @return the packages of gesture-blocked activities.
-     */
-    public List<String> getGestureBlockedActivityPackages() {
-        return mGestureBlockedActivities.stream().map(ComponentName::getPackageName)
-                .collect(Collectors.toList());
-    }
-
-    /**
      * Updates the system ui state flags from SystemUI.
      */
     public void setSystemUiFlags(int stateFlags) {
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index dc04016..825abed 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -16,9 +16,8 @@
 
 package com.android.quickstep;
 
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-
 import android.content.Context;
+import android.util.Log;
 
 import androidx.annotation.Nullable;
 
@@ -34,7 +33,9 @@
  * {@link TaskViewSimulator}
  */
 public class RemoteTargetGluer {
-    private final RemoteTargetHandle[] mRemoteTargetHandles;
+    private static final String TAG = "RemoteTargetGluer";
+
+    private RemoteTargetHandle[] mRemoteTargetHandles;
     private SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
 
     /**
@@ -93,6 +94,16 @@
     public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
         int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
                 .getRunningSplitTaskIds();
+        Log.d(TAG, "splitIds length: " + splitIds.length
+                + " targetAppsLength: " + targets.apps.length
+                + " remoteHandlesLength: " + mRemoteTargetHandles.length);
+        if (splitIds.length == 0 && mRemoteTargetHandles.length > 1) {
+            // There's a chance that between the creation of this class and assigning targets,
+            // LauncherSplitScreenListener may have received callback that removes split
+            mRemoteTargetHandles = new RemoteTargetHandle[]{mRemoteTargetHandles[0]};
+            Log.w(TAG, "splitTaskIds changed between creation and assignment");
+        }
+
         RemoteAnimationTargetCompat primaryTaskTarget;
         RemoteAnimationTargetCompat secondaryTaskTarget;
         if (mRemoteTargetHandles.length == 1) {
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index dcc7ccc..8c4ba97 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -128,7 +128,7 @@
         }
     }
 
-    class MultiWindowSystemShortcut extends SystemShortcut {
+    class MultiWindowSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
 
         private Handler mHandler;
 
@@ -305,7 +305,7 @@
         return new PinSystemShortcut(activity, taskContainer);
     };
 
-    class PinSystemShortcut extends SystemShortcut {
+    class PinSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
 
         private static final String TAG = "PinSystemShortcut";
 
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 284bc03..12b071d 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -170,7 +170,7 @@
 
         RemoteTargetHandle[] remoteTargetHandles;
         RemoteTargetHandle[] recentsViewHandles = recentsView.getRemoteTargetHandles();
-        if (v.isRunningTask()) {
+        if (v.isRunningTask() && recentsViewHandles != null) {
             // Re-use existing handles
             remoteTargetHandles = recentsViewHandles;
         } else {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index bb8473b..ddcf34c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -40,7 +40,6 @@
 import android.app.RemoteAction;
 import android.app.Service;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
@@ -67,7 +66,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
@@ -90,7 +88,6 @@
 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
-import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
@@ -103,8 +100,6 @@
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.ProxyScreenStatusProvider;
 import com.android.quickstep.util.SplitScreenBounds;
-import com.android.systemui.plugins.OverscrollPlugin;
-import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -129,8 +124,8 @@
  * Service connected by system-UI for handling touch interaction.
  */
 @TargetApi(Build.VERSION_CODES.R)
-public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
-        ProtoTraceable<LauncherTraceProto.Builder> {
+public class TouchInteractionService extends Service
+        implements ProtoTraceable<LauncherTraceProto.Builder> {
 
     private static final String TAG = "TouchInteractionService";
 
@@ -149,8 +144,8 @@
             SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
 
     private int mBackGestureNotificationCounter = -1;
-    @Nullable
-    private OverscrollPlugin mOverscrollPlugin;
+
+    private final TISBinder mTISBinder = new TISBinder();
 
     /**
      * Local IOverviewProxy implementation with some methods for local components
@@ -423,9 +418,6 @@
                 .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
 
-        PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
-                OverscrollPlugin.class, false /* allowMultiple */);
-
         mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
     }
@@ -507,7 +499,6 @@
         if (mDeviceState.isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
             mOverviewComponentObserver.onDestroy();
-            PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this);
         }
         disposeEventHandlers();
         mDeviceState.destroy();
@@ -527,7 +518,7 @@
     @Override
     public IBinder onBind(Intent intent) {
         Log.d(TAG, "Touch service connected: user=" + getUserId());
-        return new TISBinder();
+        return mTISBinder;
     }
 
     private void onInputEvent(InputEvent ev) {
@@ -691,26 +682,6 @@
                         mTaskbarManager.getCurrentActivityContext());
             }
 
-            if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
-                OverscrollPlugin plugin = null;
-                if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {
-                    plugin = OverscrollPluginFactory.INSTANCE.get(
-                            getApplicationContext()).getLocalOverscrollPlugin();
-                }
-
-                // If not local plugin was forced, use the actual overscroll plugin if available.
-                if (plugin == null && mOverscrollPlugin != null && mOverscrollPlugin.isActive()) {
-                    plugin = mOverscrollPlugin;
-                }
-
-                if (plugin != null) {
-                    // Put the overscroll gesture as higher priority than the Assistant or base
-                    // gestures
-                    base = new OverscrollInputConsumer(this, newGestureState, base,
-                        mInputMonitorCompat, plugin);
-                }
-            }
-
             // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
             // instead of going all the way home when a swipe up is detected.
             if (mDeviceState.isBubblesExpanded() || mDeviceState.isGlobalActionsShowing()) {
@@ -1002,32 +973,6 @@
                 mInputConsumer);
     }
 
-    protected boolean shouldNotifyBackGesture() {
-        return mBackGestureNotificationCounter > 0 &&
-                !mDeviceState.getGestureBlockedActivityPackages().isEmpty();
-    }
-
-    @WorkerThread
-    protected void tryNotifyBackGesture() {
-        if (shouldNotifyBackGesture()) {
-            mBackGestureNotificationCounter--;
-            Utilities.getDevicePrefs(this).edit()
-                    .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
-            mDeviceState.getGestureBlockedActivityPackages().forEach(blockedPackage ->
-                    sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(blockedPackage)));
-        }
-    }
-
-    @Override
-    public void onPluginConnected(OverscrollPlugin overscrollPlugin, Context context) {
-        mOverscrollPlugin = overscrollPlugin;
-    }
-
-    @Override
-    public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) {
-        mOverscrollPlugin = null;
-    }
-
     @Override
     public void writeToProto(LauncherTraceProto.Builder proto) {
         TouchInteractionServiceProto.Builder serviceProto =
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 725c7c4..0bd8832 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -29,7 +29,6 @@
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
-import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
 import android.annotation.TargetApi;
@@ -490,7 +489,7 @@
 
     @Override
     public boolean allowInterceptByParent() {
-        return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
+        return !mPassedPilferInputSlop;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
deleted file mode 100644
index e984b4f..0000000
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.inputconsumers;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_DOWN;
-import static android.view.MotionEvent.ACTION_POINTER_UP;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.launcher3.Utilities.squaredHypot;
-
-import static java.lang.Math.abs;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.R;
-import com.android.quickstep.GestureState;
-import com.android.quickstep.InputConsumer;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.plugins.OverscrollPlugin;
-import com.android.systemui.shared.system.InputMonitorCompat;
-
-/**
- * Input consumer for handling events to pass to an {@code OverscrollPlugin}.
- */
-public class OverscrollInputConsumer extends DelegateInputConsumer {
-    private static final String TAG = "OverscrollInputConsumer";
-    private static final boolean DEBUG_LOGS_ENABLED = false;
-    private static void debugPrint(String log) {
-        if (DEBUG_LOGS_ENABLED) {
-            Log.v(TAG, log);
-        }
-    }
-
-    private final PointF mDownPos = new PointF();
-    private final PointF mLastPos = new PointF();
-    private final PointF mStartDragPos = new PointF();
-    private final int mAngleThreshold;
-
-    private final int mFlingDistanceThresholdPx;
-    private final int mFlingVelocityThresholdPx;
-    private int mActivePointerId = -1;
-    private boolean mPassedSlop = false;
-    // True if we set ourselves as active, meaning we no longer pass events to the delegate.
-    private boolean mPassedActiveThreshold = false;
-    // When a gesture crosses this length, this recognizer will attempt to interpret touch events.
-    private final float mSquaredSlop;
-    // When a gesture crosses this length, this recognizer will become the sole active recognizer.
-    private final float mSquaredActiveThreshold;
-    // When a gesture crosses this length, the overscroll view should be shown.
-    private final float mSquaredFinishThreshold;
-    private boolean mThisDownIsIgnored = false;
-
-    private final GestureState mGestureState;
-    @Nullable
-    private final OverscrollPlugin mPlugin;
-
-    @Nullable
-    private RecentsView mRecentsView;
-
-    public OverscrollInputConsumer(Context context, GestureState gestureState,
-            InputConsumer delegate, InputMonitorCompat inputMonitor, OverscrollPlugin plugin) {
-        super(delegate, inputMonitor);
-
-        mAngleThreshold = context.getResources()
-                .getInteger(R.integer.assistant_gesture_corner_deg_threshold);
-        mFlingDistanceThresholdPx = (int) context.getResources()
-                .getDimension(R.dimen.gestures_overscroll_fling_threshold);
-        mFlingVelocityThresholdPx = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
-        mGestureState = gestureState;
-        mPlugin = plugin;
-
-        float slop = ViewConfiguration.get(context).getScaledTouchSlop();
-
-        mSquaredSlop = slop * slop;
-
-
-        float finishGestureThreshold = (int) context.getResources()
-                .getDimension(R.dimen.gestures_overscroll_finish_threshold);
-        mSquaredFinishThreshold = finishGestureThreshold * finishGestureThreshold;
-
-        float activeThreshold = (int) context.getResources()
-                .getDimension(R.dimen.gestures_overscroll_active_threshold);
-        mSquaredActiveThreshold = activeThreshold * activeThreshold;
-    }
-
-    @Override
-    public int getType() {
-        return TYPE_OVERSCROLL | mDelegate.getType();
-    }
-
-    @Override
-    public void onMotionEvent(MotionEvent ev) {
-        if (mPlugin == null) {
-            return;
-        }
-
-        debugPrint("got event, underlying activity is " + getUnderlyingActivity());
-        switch (ev.getActionMasked()) {
-            case ACTION_DOWN: {
-                debugPrint("ACTION_DOWN");
-                mActivePointerId = ev.getPointerId(0);
-                mDownPos.set(ev.getX(), ev.getY());
-                mLastPos.set(mDownPos);
-                if (mPlugin.blockOtherGestures()) {
-                    debugPrint("mPlugin.blockOtherGestures(), becoming active on ACTION_DOWN");
-                    // Otherwise, if an appear gesture is performed when the Activity is visible,
-                    // the Activity will dismiss its keyboard.
-                    mPassedActiveThreshold = true;
-                    mPassedSlop = true;
-                    mStartDragPos.set(mLastPos.x, mLastPos.y);
-                    setActive(ev);
-                }
-                break;
-            }
-            case ACTION_POINTER_DOWN: {
-                if (mState != STATE_ACTIVE) {
-                    mState = STATE_DELEGATE_ACTIVE;
-                }
-                break;
-            }
-            case ACTION_POINTER_UP: {
-                int ptrIdx = ev.getActionIndex();
-                int ptrId = ev.getPointerId(ptrIdx);
-                if (ptrId == mActivePointerId) {
-                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
-                    mDownPos.set(
-                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
-                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
-                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
-                    mActivePointerId = ev.getPointerId(newPointerIdx);
-                }
-                break;
-            }
-            case ACTION_MOVE: {
-                if (mState == STATE_DELEGATE_ACTIVE) {
-                    break;
-                }
-
-                if (!mDelegate.allowInterceptByParent()) {
-                    mState = STATE_DELEGATE_ACTIVE;
-                    break;
-                }
-
-                // Update last touch position.
-                int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex == -1) {
-                    break;
-                }
-                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-
-                float squaredDist = squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y);
-                if ((!mPassedSlop) && (squaredDist > mSquaredSlop)) {
-                    mPassedSlop = true;
-                    mStartDragPos.set(mLastPos.x, mLastPos.y);
-                    mGestureState.setState(GestureState.STATE_OVERSCROLL_WINDOW_CREATED);
-                }
-
-                boolean becomeActive = mPassedSlop && !mPassedActiveThreshold && isOverscrolled()
-                        && (squaredDist > mSquaredActiveThreshold);
-                if (becomeActive) {
-                    debugPrint("Past slop and past threshold, set active");
-                    mPassedActiveThreshold = true;
-                    setActive(ev);
-                }
-
-                if (mPassedActiveThreshold) {
-                    debugPrint("ACTION_MOVE Relaying touch event");
-                    mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
-                            (int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
-                            mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
-                }
-
-                break;
-            }
-            case ACTION_CANCEL:
-            case ACTION_UP:
-                debugPrint("ACTION_UP");
-                if (mPassedActiveThreshold) {
-                    debugPrint("ACTION_UP Relaying touch event");
-
-                    mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
-                            (int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
-                            mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
-                }
-
-
-                mPassedSlop = false;
-                mPassedActiveThreshold = false;
-                mState = STATE_INACTIVE;
-                break;
-        }
-
-        if (mState != STATE_ACTIVE) {
-            mDelegate.onMotionEvent(ev);
-        }
-    }
-
-    private boolean isOverscrolled() {
-        if (mRecentsView == null) {
-            BaseDraggingActivity activity = mGestureState.getActivityInterface()
-                    .getCreatedActivity();
-            if (activity != null) {
-                mRecentsView = activity.getOverviewPanel();
-            }
-        }
-
-        // Make sure there isn't an app to quick switch to on our right
-        int maxIndex = 0;
-
-        boolean atRightMostApp = mRecentsView == null
-                || (mRecentsView.getRunningTaskIndex() <= maxIndex);
-
-        // Check if the gesture is within our angle threshold of horizontal
-        float deltaY = abs(mLastPos.y - mDownPos.y);
-        float deltaX = mLastPos.x - mDownPos.x;
-
-        boolean angleInBounds = (Math.toDegrees(Math.atan2(deltaY, abs(deltaX))) < mAngleThreshold);
-
-        boolean overscrollVisible = mPlugin.blockOtherGestures();
-        boolean overscrollInvisibleAndLeftSwipe = !overscrollVisible && deltaX < 0;
-        boolean gestureDirectionMatchesVisibility = overscrollVisible
-                || overscrollInvisibleAndLeftSwipe;
-        return atRightMostApp && angleInBounds && gestureDirectionMatchesVisibility;
-    }
-
-    private String getDeviceState() {
-        String deviceState = OverscrollPlugin.DEVICE_STATE_UNKNOWN;
-        int consumerType = mDelegate.getType();
-        if (((consumerType & InputConsumer.TYPE_OVERVIEW) > 0)
-                || ((consumerType & InputConsumer.TYPE_OVERVIEW_WITHOUT_FOCUS)) > 0) {
-            deviceState = OverscrollPlugin.DEVICE_STATE_LAUNCHER;
-        } else if ((consumerType & InputConsumer.TYPE_OTHER_ACTIVITY) > 0) {
-            deviceState = OverscrollPlugin.DEVICE_STATE_APP;
-        } else if (((consumerType & InputConsumer.TYPE_RESET_GESTURE) > 0)
-                || ((consumerType & InputConsumer.TYPE_DEVICE_LOCKED) > 0)) {
-            deviceState = OverscrollPlugin.DEVICE_STATE_LOCKED;
-        }
-
-        return deviceState;
-    }
-
-    private int getHorizontalDistancePx() {
-        return (int) (mLastPos.x - mDownPos.x);
-    }
-
-    private int getVerticalDistancePx() {
-        return (int) (mLastPos.y - mDownPos.y);
-    }
-
-    private @NonNull String getUnderlyingActivity() {
-        // Overly defensive, got guidance on code review that something in the chain of
-        // `mGestureState.getRunningTask().topActivity` can be null and thus cause a null pointer
-        // exception to be thrown, but we aren't sure which part can be null.
-        if ((mGestureState == null) || (mGestureState.getRunningTask() == null)
-                || (mGestureState.getRunningTask().topActivity == null)) {
-            return "";
-        }
-        return mGestureState.getRunningTask().topActivity.flattenToString();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 4472bdc..f731cb3 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -32,6 +32,8 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.TISBindHelper;
 
 import java.net.URISyntaxException;
 
@@ -47,6 +49,9 @@
     private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
     private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
 
+    private TISBindHelper mTISBindHelper;
+    private TISBinder mBinder;
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -73,6 +78,34 @@
         });
 
         findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mBinder != null) {
+            mBinder.getTaskbarManager().setSetupUIVisible(true);
+        }
+    }
+
+    private void onTISConnected(TISBinder binder) {
+        mBinder = binder;
+        mBinder.getTaskbarManager().setSetupUIVisible(isResumed());
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mBinder != null) {
+            mBinder.getTaskbarManager().setSetupUIVisible(false);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mTISBindHelper.onDestroy();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 30f9008..fb6cd8a 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -49,12 +49,16 @@
 
     @LayoutRes
     int getMockAppTaskCurrentPageLayoutResId() {
-        return R.layout.gesture_tutorial_mock_conversation;
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_conversation
+                : R.layout.gesture_tutorial_mock_conversation;
     }
 
     @LayoutRes
     int getMockAppTaskPreviousPageLayoutResId() {
-        return R.layout.gesture_tutorial_mock_conversation_list;
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_conversation_list
+                : R.layout.gesture_tutorial_mock_conversation_list;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 3ad84f0..a45f273 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -43,7 +43,9 @@
 
     @Override
     protected int getMockAppTaskLayoutResId() {
-        return R.layout.gesture_tutorial_mock_webpage;
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_webpage
+                : R.layout.gesture_tutorial_mock_webpage;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 9d60e1b..24ef1fa 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -53,7 +53,9 @@
 
     @Override
     protected int getMockAppTaskLayoutResId() {
-        return R.layout.gesture_tutorial_mock_conversation_list;
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_conversation_list
+                : R.layout.gesture_tutorial_mock_conversation_list;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index 968412b..57a76ca 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -57,17 +57,6 @@
             }
         });
 
-        Animator swipeAnimator =
-                controller.createFingerDotOverviewSwipeAnimator(fingerDotStartTranslationY);
-        swipeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                mFakePreviousTaskView.setVisibility(View.VISIBLE);
-                controller.onMotionPaused(true /*arbitrary value*/);
-            }
-        });
-
         AnimatorSet fingerDotDisappearanceAnimator =
                 controller.createFingerDotDisappearanceAnimatorSet();
         fingerDotDisappearanceAnimator.addListener(new AnimatorListenerAdapter() {
@@ -89,7 +78,7 @@
         ArrayList<Animator> animators = new ArrayList<>();
 
         animators.add(fingerDotAppearanceAnimator);
-        animators.add(swipeAnimator);
+        animators.add(controller.createFingerDotOverviewSwipeAnimator(fingerDotStartTranslationY));
         animators.add(controller.createAnimationPause());
         animators.add(fingerDotDisappearanceAnimator);
         animators.add(animationPause);
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 0c7b35b..a923519 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -320,8 +320,8 @@
                 @Override
                 public RectF getWindowTargetRect() {
                     int fakeHomeIconSizePx = Utilities.dpToPx(60);
-                    int fakeHomeIconLeft = mFakeHotseatView.getLeft();
-                    int fakeHomeIconTop = mFakeHotseatView.getTop();
+                    int fakeHomeIconLeft = getHotseatIconLeft();
+                    int fakeHomeIconTop = getHotseatIconTop();
                     return new RectF(fakeHomeIconLeft, fakeHomeIconTop,
                             fakeHomeIconLeft + fakeHomeIconSizePx,
                             fakeHomeIconTop + fakeHomeIconSizePx);
@@ -374,8 +374,19 @@
     }
 
     protected Animator createFingerDotOverviewSwipeAnimator(float fingerDotStartTranslationY) {
-        return createFingerDotSwipeUpAnimator(fingerDotStartTranslationY)
+        Animator overviewSwipeAnimator = createFingerDotSwipeUpAnimator(fingerDotStartTranslationY)
                 .setDuration(OVERVIEW_SWIPE_ANIMATION_DURATION_MILLIS);
+
+        overviewSwipeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mFakePreviousTaskView.setVisibility(View.VISIBLE);
+                onMotionPaused(true /*arbitrary value*/);
+            }
+        });
+
+        return overviewSwipeAnimator;
     }
 
 
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 94fb556..9c1ff4d 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -85,7 +85,8 @@
     final TextView mFeedbackTitleView;
     final ImageView mEdgeGestureVideoView;
     final RelativeLayout mFakeLauncherView;
-    final ImageView mFakeHotseatView;
+    final FrameLayout mFakeHotseatView;
+    @Nullable View mHotseatIconView;
     final ClipIconView mFakeIconView;
     final FrameLayout mFakeTaskView;
     final AnimatedTaskView mFakePreviousTaskView;
@@ -164,13 +165,25 @@
         }
     }
 
+    public int getHotseatIconTop() {
+        return mHotseatIconView == null
+                ? 0 : mFakeHotseatView.getTop() + mHotseatIconView.getTop();
+    }
+
+    public int getHotseatIconLeft() {
+        return mHotseatIconView == null
+                ? 0 : mFakeHotseatView.getLeft() + mHotseatIconView.getLeft();
+    }
+
     void setTutorialType(TutorialType tutorialType) {
         mTutorialType = tutorialType;
     }
 
-    @DrawableRes
+    @LayoutRes
     protected int getMockHotseatResId() {
-        return R.drawable.default_sandbox_mock_launcher;
+        return mTutorialFragment.isLargeScreen()
+                ? R.layout.gesture_tutorial_foldable_mock_hotseat
+                : R.layout.gesture_tutorial_mock_hotseat;
     }
 
     @LayoutRes
@@ -384,6 +397,7 @@
         hideActionButton();
         updateSubtext();
         updateDrawables();
+        updateLayout();
 
         mGestureCompleted = false;
         if (mFakeHotseatView != null) {
@@ -416,10 +430,14 @@
     }
 
     void updateFakeAppTaskViewLayout(@LayoutRes int mockAppTaskLayoutResId) {
-        mFakeTaskView.removeAllViews();
-        if (mockAppTaskLayoutResId != NO_ID) {
-            mFakeTaskView.addView(
-                    inflate(mContext, mockAppTaskLayoutResId, null),
+        updateFakeViewLayout(mFakeTaskView, mockAppTaskLayoutResId);
+    }
+
+    void updateFakeViewLayout(ViewGroup view, @LayoutRes int mockLayoutResId) {
+        view.removeAllViews();
+        if (mockLayoutResId != NO_ID) {
+            view.addView(
+                    inflate(mContext, mockLayoutResId, null),
                     new FrameLayout.LayoutParams(
                             ViewGroup.LayoutParams.MATCH_PARENT,
                             ViewGroup.LayoutParams.MATCH_PARENT));
@@ -438,9 +456,9 @@
             mTutorialFragment.updateFeedbackAnimation();
             mFakeLauncherView.setBackgroundColor(
                     mContext.getColor(R.color.gesture_tutorial_fake_wallpaper_color));
-            mFakeHotseatView.setImageDrawable(AppCompatResources.getDrawable(
-                    mContext, getMockHotseatResId()));
-            updateFakeAppTaskViewLayout(getMockAppTaskLayoutResId());
+            updateFakeViewLayout(mFakeHotseatView, getMockHotseatResId());
+            mHotseatIconView = mFakeHotseatView.findViewById(R.id.hotseat_icon_1);
+            updateFakeViewLayout(mFakeTaskView, getMockAppTaskLayoutResId());
             mFakeTaskView.animate().alpha(1).setListener(
                     AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel()));
             mFakePreviousTaskView.setFakeTaskViewFillColor(mContext.getResources().getColor(
@@ -450,6 +468,21 @@
         }
     }
 
+    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));
+        }
+    }
+
     private AlertDialog createSkipTutorialDialog() {
         if (mContext instanceof GestureSandboxActivity) {
             GestureSandboxActivity sandboxActivity = (GestureSandboxActivity) mContext;
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
new file mode 100644
index 0000000..92c60c8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -0,0 +1,124 @@
+/*
+ * 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.quickstep.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+
+import java.util.function.Consumer;
+
+/**
+ * Utility class to simplify binding to {@link TouchInteractionService}
+ */
+public class TISBindHelper implements ServiceConnection {
+
+    private static final String TAG = "TISBindHelper";
+
+    private static final long BACKOFF_MILLIS = 1000;
+
+    // Max backoff caps at 5 mins
+    private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mConnectionRunnable = this::internalBindToTIS;
+    private final Context mContext;
+    private final Consumer<TISBinder> mConnectionCallback;
+
+    private short mConnectionAttempts;
+    private boolean mTisServiceBound;
+
+    public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) {
+        mContext = context;
+        mConnectionCallback = connectionCallback;
+        internalBindToTIS();
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+        if (!(iBinder instanceof TISBinder)) {
+            // Seems like there can be a race condition when user unlocks, which kills the TIS
+            // process and re-starts it. I guess in the meantime service can be connected to
+            // a killed TIS? Either way, unbind and try to re-connect in that case.
+            internalUnbindToTIS();
+            mHandler.postDelayed(mConnectionRunnable, BACKOFF_MILLIS);
+            return;
+        }
+
+        Log.d(TAG, "TIS service connected");
+        mConnectionCallback.accept((TISBinder) iBinder);
+        resetServiceBindRetryState();
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName componentName) { }
+
+    @Override
+    public void onBindingDied(ComponentName name) {
+        Log.w(TAG, "TIS binding died");
+        internalBindToTIS();
+    }
+
+
+    /**
+     * Binds to {@link TouchInteractionService}. If the binding fails, attempts to retry via
+     * {@link #mConnectionRunnable}. Unbind via {@link #internalUnbindToTIS()}
+     */
+    private void internalBindToTIS() {
+        mTisServiceBound = mContext.bindService(new Intent(mContext, TouchInteractionService.class),
+                this, 0);
+        if (mTisServiceBound) {
+            resetServiceBindRetryState();
+            return;
+        }
+
+        Log.w(TAG, "Retrying TIS Binder connection attempt: " + mConnectionAttempts);
+        final long timeoutMs = (long) Math.min(
+                Math.scalb(BACKOFF_MILLIS, mConnectionAttempts), MAX_BACKOFF_MILLIS);
+        mHandler.postDelayed(mConnectionRunnable, timeoutMs);
+        mConnectionAttempts++;
+    }
+
+    /** See {@link #internalBindToTIS()} */
+    private void internalUnbindToTIS() {
+        if (mTisServiceBound) {
+            mContext.unbindService(this);
+            mTisServiceBound = false;
+        }
+    }
+
+    private void resetServiceBindRetryState() {
+        if (mHandler.hasCallbacks(mConnectionRunnable)) {
+            mHandler.removeCallbacks(mConnectionRunnable);
+        }
+        mConnectionAttempts = 0;
+    }
+
+    /**
+     * Called when the activity is destroyed to clear the binding
+     */
+    public void onDestroy() {
+        internalUnbindToTIS();
+        resetServiceBindRetryState();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 4d488db..292e9d7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1484,6 +1484,20 @@
         return groupViewCount;
     }
 
+    /**
+     * Returns the number of tasks in the top row of the overview grid.
+     */
+    public int getTopRowTaskCountForTablet() {
+        return mTopRowIdSet.size();
+    }
+
+    /**
+     * Returns the number of tasks in the bottom row of the overview grid.
+     */
+    public int getBottomRowTaskCountForTablet() {
+        return getTaskViewCount() - mTopRowIdSet.size() - 1;
+    }
+
     protected void onTaskStackUpdated() {
         // Lazily update the empty message only when the task stack is reapplied
         updateEmptyMessage();
@@ -3170,7 +3184,7 @@
      * Returns all the tasks in the bottom row, without the focused task
      */
     private IntArray getBottomRowIdArray() {
-        int bottomRowIdArraySize = getTaskViewCount() - mTopRowIdSet.size() - 1;
+        int bottomRowIdArraySize = getBottomRowTaskCountForTablet();
         if (bottomRowIdArraySize <= 0) {
             return new IntArray(0);
         }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 3b7370f..4895b10 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -146,7 +146,8 @@
 
         // Test dismissing all tasks.
         mLauncher.pressHome().switchToOverview().dismissAllTasks();
-        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+        assertTrue("Launcher internal state is not Home",
+                isInState(() -> LauncherState.NORMAL));
         executeOnLauncher(
                 launcher -> assertEquals("Still have tasks after dismissing all",
                         0, getTaskCount(launcher)));
@@ -180,6 +181,14 @@
         return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
     }
 
+    private int getTopRowTaskCountForTablet(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getTopRowTaskCountForTablet();
+    }
+
+    private int getBottomRowTaskCountForTablet(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
+    }
+
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
@@ -276,4 +285,70 @@
                 isTestActivityRunning(2));
         getAndAssertBackground();
     }
+
+    @Test
+    @PortraitLandscape
+    public void testOverviewForTablet() throws Exception {
+        if (!mLauncher.isTablet()) {
+            return;
+        }
+        for (int i = 2; i <= 12; i++) {
+            startTestActivity(i);
+        }
+
+        Overview overview = mLauncher.pressHome().switchToOverview();
+        executeOnLauncher(
+                launcher -> assertTrue("Don't have at least 11 tasks",
+                        getTaskCount(launcher) >= 11));
+
+        // Test scroll the first task off screen
+        overview.scrollCurrentTaskOffScreen();
+        assertTrue("Launcher internal state is not Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+                getCurrentOverviewPage(launcher) > 0));
+
+        // Test opening the task.
+        overview.getCurrentTask().open();
+        assertTrue("Test activity didn't open from Overview",
+                mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity8")),
+                        DEFAULT_UI_TIMEOUT));
+
+        // Scroll the task offscreen as it is now first
+        overview = mLauncher.pressHome().switchToOverview();
+        overview.scrollCurrentTaskOffScreen();
+        assertTrue("Launcher internal state is not Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+                getCurrentOverviewPage(launcher) > 0));
+
+        // Test dismissing the later task.
+        final Integer numTasks = getFromLauncher(this::getTaskCount);
+        overview.getCurrentTask().dismiss();
+        executeOnLauncher(
+                launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
+                        numTasks - 1, getTaskCount(launcher)));
+        executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after dismissal",
+                (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
+                        launcher)) <= 1)));
+
+        // Test dismissing more tasks.
+        assertTrue("Launcher internal state didn't remain in Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        overview.getCurrentTask().dismiss();
+        assertTrue("Launcher internal state didn't remain in Overview",
+                isInState(() -> LauncherState.OVERVIEW));
+        overview.getCurrentTask().dismiss();
+        executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals",
+                (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
+                        launcher)) <= 1)));
+
+        // Test dismissing all tasks.
+        mLauncher.pressHome().switchToOverview().dismissAllTasks();
+        assertTrue("Launcher internal state is not Home",
+                isInState(() -> LauncherState.NORMAL));
+        executeOnLauncher(
+                launcher -> assertEquals("Still have tasks after dismissing all",
+                        0, getTaskCount(launcher)));
+    }
 }
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 9ac6ed0..a34baef 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -42,10 +42,19 @@
         <include layout="@layout/floating_header_content" />
 
         <include layout="@layout/all_apps_personal_work_tabs" />
+
+        <Button
+            android:id="@+id/all_apps_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:text="@string/all_apps_label"
+            android:background="@drawable/padded_rounded_action_button"
+            android:visibility="gone"/>
+
     </com.android.launcher3.allapps.FloatingHeaderView>
 
-    <include
-        layout="@layout/search_container_all_apps"/>
+    <include layout="@layout/search_container_all_apps" />
 
     <include layout="@layout/all_apps_fast_scroller" />
 </com.android.launcher3.allapps.LauncherAllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/system_shortcut_content.xml b/res/layout/system_shortcut_content.xml
index 3ef0b94..e693dbd 100644
--- a/res/layout/system_shortcut_content.xml
+++ b/res/layout/system_shortcut_content.xml
@@ -32,6 +32,7 @@
         android:minLines="1"
         android:maxLines="2"
         android:ellipsize="end"
+        android:hyphenationFrequency="full"
         android:textColor="?android:attr/textColorPrimary"
         launcher:iconDisplay="shortcut_popup"
         launcher:layoutHorizontal="true"
@@ -44,4 +45,4 @@
         android:layout_marginStart="@dimen/system_shortcut_margin_start"
         android:layout_gravity="start|center_vertical"
         android:backgroundTint="?android:attr/textColorPrimary"/>
-</merge>
\ No newline at end of file
+</merge>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index b6acb97..d56d898 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -41,7 +41,7 @@
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟ"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ਖੋਜੋ"</string>
-    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ਖੋਜ ਬਾਕਸ ਤੋਂ ਸਪੱਸ਼ਟ ਲਿਖਤ"</string>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ਖੋਜ ਬਾਕਸ ਤੋਂ ਲਿਖਤ ਕਲੀਅਰ ਕਰੋ"</string>
     <string name="no_widgets_available" msgid="4337693382501046170">"ਵਿਜੇਟ ਜਾਂ ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹਨ"</string>
     <string name="no_search_results" msgid="3787956167293097509">"ਕੋਈ ਵੀ ਵਿਜੇਟ ਜਾਂ ਸ਼ਾਰਟਕੱਟ ਨਹੀਂ ਮਿਲਿਆ"</string>
     <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ਨਿੱਜੀ"</string>
@@ -115,7 +115,7 @@
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।"</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਹੋਈ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਨੂੰ ਹੱਥੀਂ ਖੋਜ ਕੇ ਉਸਨੂੰ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।"</string>
     <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ਨੂੰ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਪੂਰਾ ਹੋਇਆ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b1294b4..4d137c8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -103,6 +103,7 @@
     <dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
     <dimen name="all_apps_divider_height">2dp</dimen>
     <dimen name="all_apps_divider_width">128dp</dimen>
+    <dimen name="all_apps_content_fade_in_offset">150dp</dimen>
 
     <dimen name="all_apps_tip_bottom_margin">8dp</dimen>
     <!-- The size of corner radius of the arrow in the arrow toast. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f315725..d7a1506 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 * Copyright (C) 2008 The Android Open Source Project
 *
@@ -132,6 +131,8 @@
     <string name="all_apps_search_market_message">Search for more apps</string>
     <!-- Label for an icon representing any generic app. [CHAR_LIMIT=50] -->
     <string name="label_application">App</string>
+    <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
+    <string name="all_apps_label">All apps</string>
 
     <!-- Popup items -->
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
@@ -193,7 +194,7 @@
     <string name="msg_no_phone_permission"><xliff:g id="app_name" example="Launcher3">%1$s</xliff:g> is not allowed to make phone calls</string>
 
     <!-- Widgets: -->
-    <skip />    
+    <skip />
 
     <!-- Error text that lets a user know that the widget can't load. -->
     <string name="gadget_error_text">Can\'t load widget</string>
@@ -308,7 +309,7 @@
     <!-- Text announced by accessibility when the popup containing the list of widgets is closed. [CHAR_LIMIT=100] -->
     <string name="widgets_list_closed">Widgets list closed</string>
 
-<!-- Strings for accessibility actions -->
+    <!-- Strings for accessibility actions -->
     <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
     <string name="action_add_to_workspace">Add to Home screen</string>
 
diff --git a/res/xml/size_limits_80x104.xml b/res/xml/size_limits_80x104.xml
deleted file mode 100644
index 4178664..0000000
--- a/res/xml/size_limits_80x104.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
-
-    <device-padding
-        launcher:maxEmptySpace="88dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="0"/>
-        <workspaceBottomPadding
-            launcher:a="0.52"
-            launcher:b="0"/>
-        <hotseatBottomPadding
-            launcher:a="0.48"
-            launcher:b="0"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="100dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="9dp"/>
-        <workspaceBottomPadding
-            launcher:a="0.40"
-            launcher:b="0"
-            launcher:c="9dp"/>
-        <hotseatBottomPadding
-            launcher:a="0.60"
-            launcher:b="0"
-            launcher:c="9dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="103dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="26dp"/>
-        <workspaceBottomPadding
-            launcher:a="0"
-            launcher:b="20dp"/>
-        <hotseatBottomPadding
-            launcher:a="1"
-            launcher:b="0"
-            launcher:c="46dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="107dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="9dp"/>
-        <workspaceBottomPadding
-            launcher:a="0"
-            launcher:b="34dp"/>
-        <hotseatBottomPadding
-            launcher:a="1"
-            launcher:b="0"
-            launcher:c="43dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="120dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="16dp"/>
-        <workspaceBottomPadding
-            launcher:a="1"
-            launcher:c="72dp"/>
-        <hotseatBottomPadding
-            launcher:a="0"
-            launcher:b="56dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="135dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="39dp"/>
-        <workspaceBottomPadding
-            launcher:a="1"
-            launcher:c="95dp"/>
-        <hotseatBottomPadding
-            launcher:a="0"
-            launcher:b="56dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="9999dp">
-        <workspaceTopPadding
-            launcher:a="0.40"
-            launcher:c="36dp"/>
-        <workspaceBottomPadding
-            launcher:a="0.60"
-            launcher:c="36dp"/>
-        <hotseatBottomPadding
-            launcher:a="0"
-            launcher:b="36dp"/>
-    </device-padding>
-</device-paddings>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ebfd281..1bfd7b5 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -215,7 +215,7 @@
 
         dl.addView(frame);
         frame.mIsOpen = true;
-        frame.snapToWidget(false);
+        frame.post(() -> frame.snapToWidget(false));
     }
 
     private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 7954011..dd56ca3 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -41,7 +41,6 @@
 import android.view.ActionMode;
 import android.view.Display;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.WindowInsets.Type;
 import android.view.WindowMetrics;
 import android.widget.Toast;
@@ -166,12 +165,6 @@
         // no-op
     }
 
-    public Rect getViewBounds(View v) {
-        int[] pos = new int[2];
-        v.getLocationOnScreen(pos);
-        return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
-    }
-
     @NonNull
     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
         int left = 0, top = 0;
@@ -206,7 +199,7 @@
         // Prepare intent
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (v != null) {
-            intent.setSourceBounds(getViewBounds(v));
+            intent.setSourceBounds(Utilities.getViewBounds(v));
         }
         try {
             boolean isShortcut = (item instanceof WorkspaceItemInfo)
@@ -316,7 +309,8 @@
         }
     }
 
-    public OnClickListener getItemOnClickListener() {
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
         return ItemClickHandler.INSTANCE;
     }
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 521d8f4..7811047 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
@@ -33,6 +34,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.icu.text.MessageFormat;
+import android.text.TextPaint;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -66,6 +68,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BubbleTextHolder;
 import com.android.launcher3.views.IconLabelDotView;
 
 import java.text.NumberFormat;
@@ -87,6 +90,9 @@
     private static final int DISPLAY_SEARCH_RESULT = 6;
     private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
 
+    private static final float MIN_LETTER_SPACING = -0.5f;
+    private static final int MAX_SEARCH_LOOP_COUNT = 20;
+
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
     private static final float HIGHLIGHT_SCALE = 1.16f;
 
@@ -162,7 +168,7 @@
     private HandlerRunnable mIconLoadRequest;
 
     private boolean mEnableIconUpdateAnimation = false;
-    private ItemInfoUpdateReceiver mItemInfoUpdateReceiver;
+    private BubbleTextHolder mBubbleTextHolder;
 
     public BubbleTextView(Context context) {
         this(context, null, 0);
@@ -240,7 +246,6 @@
         mDotParams.scale = 0f;
         mForceHideDot = false;
         setBackground(null);
-        mItemInfoUpdateReceiver = null;
     }
 
     private void cancelDotScaleAnim() {
@@ -340,14 +345,14 @@
 
     private void setItemInfo(ItemInfoWithIcon itemInfo) {
         setTag(itemInfo);
-        if (mItemInfoUpdateReceiver != null) {
-            mItemInfoUpdateReceiver.reapplyItemInfo(itemInfo);
+        if (mBubbleTextHolder != null) {
+            mBubbleTextHolder.onItemInfoUpdated(itemInfo);
         }
     }
 
-    public void setItemInfoUpdateReceiver(
-            ItemInfoUpdateReceiver itemInfoUpdateReceiver) {
-        mItemInfoUpdateReceiver = itemInfoUpdateReceiver;
+    public void setBubbleTextHolder(
+            BubbleTextHolder bubbleTextHolder) {
+        mBubbleTextHolder = bubbleTextHolder;
     }
 
     @UiThread
@@ -460,6 +465,75 @@
         return result;
     }
 
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        checkForEllipsis();
+    }
+
+    @Override
+    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+        checkForEllipsis();
+    }
+
+    private void checkForEllipsis() {
+        if (!ENABLE_ICON_LABEL_AUTO_SCALING.get()) {
+            return;
+        }
+        float width = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
+        if (width <= 0) {
+            return;
+        }
+        setLetterSpacing(0);
+
+        String text = getText().toString();
+        TextPaint paint = getPaint();
+        if (paint.measureText(text) < width) {
+            return;
+        }
+
+        float spacing = findBestSpacingValue(paint, text, width, MIN_LETTER_SPACING);
+        // Reset the paint value so that the call to TextView does appropriate diff.
+        paint.setLetterSpacing(0);
+        setLetterSpacing(spacing);
+    }
+
+    /**
+     * Find the appropriate text spacing to display the provided text
+     * @param paint the paint used by the text view
+     * @param text the text to display
+     * @param allowedWidthPx available space to render the text
+     * @param minSpacingEm minimum spacing allowed between characters
+     * @return the final textSpacing value
+     *
+     * @see #setLetterSpacing(float)
+     */
+    private float findBestSpacingValue(TextPaint paint, String text, float allowedWidthPx,
+            float minSpacingEm) {
+        paint.setLetterSpacing(minSpacingEm);
+        if (paint.measureText(text) > allowedWidthPx) {
+            // If there is no result at high limit, we can do anything more
+            return minSpacingEm;
+        }
+
+        float lowLimit = 0;
+        float highLimit = minSpacingEm;
+
+        for (int i = 0; i < MAX_SEARCH_LOOP_COUNT; i++) {
+            float value = (lowLimit + highLimit) / 2;
+            paint.setLetterSpacing(value);
+            if (paint.measureText(text) < allowedWidthPx) {
+                highLimit = value;
+            } else {
+                lowLimit = value;
+            }
+        }
+
+        // At the end error on the higher side
+        return highLimit;
+    }
+
     @SuppressWarnings("wrongcall")
     protected void drawWithoutDot(Canvas canvas) {
         super.onDraw(canvas);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index e86c02c..ffe3816 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -198,6 +198,10 @@
         getShortcutsAndWidgets().setAlpha(alpha);
     }
 
+    public float getIconsAlpha() {
+        return getShortcutsAndWidgets().getAlpha();
+    }
+
     /**
      * Returns the QSB inside hotseat
      */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f429d76..8d92bf2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1391,22 +1391,7 @@
             final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView;
             CellLayout cellLayout = getCellLayout(launcherInfo.container, launcherInfo.screenId);
             if (mStateManager.getState() == NORMAL) {
-                // Show resize frame once the widget layout is drawn.
-                View.OnLayoutChangeListener onLayoutChangeListener =
-                        new View.OnLayoutChangeListener() {
-                            @Override
-                            public void onLayoutChange(View view, int left, int top, int right,
-                                    int bottom, int oldLeft, int oldTop, int oldRight,
-                                    int oldBottom) {
-                                AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
-                                launcherHostView.removeOnLayoutChangeListener(this);
-                            }
-                        };
-                launcherHostView.addOnLayoutChangeListener(onLayoutChangeListener);
-                // There is a small chance that the layout was already drawn before the layout
-                // change listener was registered, which means that the resize frame wouldn't be
-                // shown. Directly call requestLayout to force a layout change.
-                launcherHostView.requestLayout();
+                AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
             } else {
                 mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
                     @Override
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 7de2ee4..5ef3690 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,5 +1,6 @@
 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;
@@ -99,7 +100,11 @@
                 oldNavInsets.bottom);
 
         if (dp.isLandscape) {
-            if (dp.isTablet) {
+            boolean isGesturalMode = ResourceUtils.getIntegerByName(
+                    "config_navBarInteractionMode",
+                    resources,
+                    INVALID_RESOURCE_HANDLE) == 2;
+            if (dp.isTablet || isGesturalMode) {
                 newNavInsets.bottom = ResourceUtils.getNavbarSize(
                         "navigation_bar_height_landscape", resources);
             } else {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 36faeee..7a38fe7 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -69,6 +69,7 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
 import androidx.core.graphics.ColorUtils;
 import androidx.core.os.BuildCompat;
 
@@ -846,6 +847,12 @@
         view.setLayoutParams(lp);
     }
 
+    public static Rect getViewBounds(@NonNull View v) {
+        int[] pos = new int[2];
+        v.getLocationOnScreen(pos);
+        return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+    }
+
     private static class FixedSizeEmptyDrawable extends ColorDrawable {
 
         private final int mSize;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8e76d82..8095280 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1676,7 +1676,7 @@
         }
 
         if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
-            PopupContainerWithArrow popupContainer = PopupContainerWithArrow
+            PopupContainerWithArrow<Launcher> popupContainer = PopupContainerWithArrow
                     .showForIcon((BubbleTextView) child);
             if (popupContainer != null) {
                 dragOptions.preDragCondition = popupContainer.createPreDragCondition();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index f154dd4..3ba6ea4 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -485,8 +485,9 @@
             mViewPager = (AllAppsPagedView) newView;
             mViewPager.initParentViews(this);
             mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
-            mWorkManager.attachWorkModeSwitch();
-            mWorkManager.getWorkModeSwitch().post(() -> mAH[AdapterHolder.WORK].applyPadding());
+            if (mWorkManager.attachWorkModeSwitch()) {
+                mWorkManager.getWorkModeSwitch().post(() -> mAH[AdapterHolder.WORK].applyPadding());
+            }
         } else {
             mWorkManager.detachWorkModeSwitch();
             mViewPager = null;
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index debb5b2..3ca0303 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -47,6 +47,7 @@
         ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
         OnHeightUpdatedListener {
 
+    private static final long ALL_APPS_CONTENT_ANIM_DURATION = 150;
     private final Rect mRVClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final Rect mHeaderClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
@@ -108,6 +109,14 @@
     // enabled or disabled, and represent the current set of all rows.
     private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
 
+
+    // members for handling suggestion state
+    private final ValueAnimator mAllAppsContentAnimator = ValueAnimator.ofFloat(0, 0);
+    private View mAllAppsButton;
+    private int mAllAppsContentFadeInOffset;
+    private boolean mInSuggestionMode = false;
+
+
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
     }
@@ -118,12 +127,20 @@
                 .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
         mHeaderProtectionSupported = context.getResources().getBoolean(
                 R.bool.config_header_protection_supported);
+        mAllAppsContentFadeInOffset = context.getResources()
+                .getDimensionPixelSize(R.dimen.all_apps_content_fade_in_offset);
+        mAllAppsContentAnimator.setDuration(ALL_APPS_CONTENT_ANIM_DURATION);
+        mAllAppsContentAnimator.addUpdateListener(this::onAllAppsContentAnimationUpdate);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTabLayout = findViewById(R.id.tabs);
+        mAllAppsButton = findViewById(R.id.all_apps_button);
+        if (mAllAppsButton != null) {
+            mAllAppsButton.setOnClickListener(this::onAllAppsButtonClicked);
+        }
 
         // Find all floating header rows.
         ArrayList<FloatingHeaderRow> rows = new ArrayList<>();
@@ -312,6 +329,7 @@
         }
 
         mTabLayout.setTranslationY(mTranslationY);
+        setSuggestionMode(false);
 
         int clipHeight = mHeaderTopPadding - getPaddingBottom();
         mRVClip.top = mTabsHidden ? clipHeight : 0;
@@ -347,6 +365,7 @@
             mTranslationY = 0;
             applyVerticalMove();
         }
+        setSuggestionMode(false);
         mHeaderCollapsed = false;
         mSnappedScrolledY = -mMaxTranslation;
         mCurrentRV.scrollToTop();
@@ -442,6 +461,38 @@
         }
         return Math.max(getHeight() - getPaddingTop() + mTranslationY, 0);
     }
+
+    /**
+     * When suggestion mode is enabled, hides AllApps content view and shows AllApps button.
+     */
+    public void setSuggestionMode(boolean isSuggestMode) {
+        if (mInSuggestionMode == isSuggestMode || mAllAppsButton == null) return;
+        if (!FeatureFlags.ENABLE_ONE_SEARCH.get()) return;
+        AllAppsContainerView allApps = (AllAppsContainerView) getParent();
+        mInSuggestionMode = isSuggestMode;
+        if (isSuggestMode) {
+            mTabLayout.setVisibility(GONE);
+            mAllAppsButton.setVisibility(VISIBLE);
+            allApps.getContentView().setVisibility(GONE);
+        } else {
+            mTabLayout.setVisibility(mTabsHidden ? GONE : VISIBLE);
+            mAllAppsButton.setVisibility(GONE);
+            allApps.getContentView().setVisibility(VISIBLE);
+        }
+    }
+
+    private void onAllAppsButtonClicked(View view) {
+        setSuggestionMode(false);
+        mAllAppsContentAnimator.start();
+    }
+
+    private void onAllAppsContentAnimationUpdate(ValueAnimator valueAnimator) {
+        float prog = valueAnimator.getAnimatedFraction();
+        View allAppsList = ((AllAppsContainerView) getParent()).getContentView();
+        allAppsList.setAlpha(255 * prog);
+        allAppsList.setTranslationY((1 - prog) * mAllAppsContentFadeInOffset);
+    }
+
 }
 
 
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index 54a5c51..e223248 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.R;
@@ -127,11 +128,11 @@
     /**
      * Creates and attaches for profile toggle button to {@link AllAppsContainerView}
      */
-    public void attachWorkModeSwitch() {
+    public boolean attachWorkModeSwitch() {
         if (!mAllApps.getAppsStore().hasModelFlag(
                 FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION)) {
-            Log.e(TAG, "Unable to attach widget; Missing required permissions");
-            return;
+            Log.e(TAG, "unable to attach work mode switch; Missing required permissions");
+            return false;
         }
         if (mWorkModeSwitch == null) {
             mWorkModeSwitch = (WorkModeSwitch) mAllApps.getLayoutInflater().inflate(
@@ -144,6 +145,7 @@
             getAH().applyPadding();
         }
         mWorkModeSwitch.updateCurrentState(mCurrentState == STATE_ENABLED);
+        return true;
     }
 
     /**
@@ -165,6 +167,7 @@
         return mMatcher;
     }
 
+    @Nullable
     public WorkModeSwitch getWorkModeSwitch() {
         return mWorkModeSwitch;
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ec077de..796c912 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,12 +72,6 @@
             "PROMISE_APPS_NEW_INSTALLS", true,
             "Adds a promise icon to the home screen for new install sessions.");
 
-    public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
-            "QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
-
-    public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
-            "UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
-
     public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
             "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
 
@@ -122,16 +116,6 @@
     public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
             "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
 
-    public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
-            "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
-
-    public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag(
-            "ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture");
-
-    public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
-            "FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
-            "Use a launcher-provided OverscrollPlugin if available");
-
     public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
@@ -160,7 +144,7 @@
             "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
 
     public static final BooleanFlag ENABLE_WIDGETS_PICKER_AIAI_SEARCH = new DeviceFlag(
-            "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", false, "Enable AiAi search in the widgets picker");
+            "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", true, "Enable AiAi search in the widgets picker");
 
     public static final BooleanFlag ENABLE_OVERVIEW_SHARING_TO_PEOPLE = getDebugFlag(
             "ENABLE_OVERVIEW_SHARING_TO_PEOPLE", true,
@@ -268,6 +252,10 @@
             "ENABLE_BACK_SWIPE_HOME_ANIMATION", true,
             "Enables home animation to icon when user swipes back.");
 
+    public static final BooleanFlag ENABLE_ICON_LABEL_AUTO_SCALING = getDebugFlag(
+            "ENABLE_ICON_LABEL_AUTO_SCALING", true,
+            "Enables scaling/spacing for icon labels to make more characters visible");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index d6e927b..b963950 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -66,6 +66,7 @@
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.ShortcutUtil;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
@@ -81,7 +82,7 @@
  *
  * @param <T> The activity on with the popup shows
  */
-public class PopupContainerWithArrow<T extends BaseDraggingActivity>
+public class PopupContainerWithArrow<T extends Context & ActivityContext>
         extends ArrowPopup<T> implements DragSource, DragController.DragListener {
 
     private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
@@ -190,10 +191,10 @@
     }
 
     /**
-     * Shows the notifications and deep shortcuts associated with {@param icon}.
+     * Shows the notifications and deep shortcuts associated with a Launcher {@param icon}.
      * @return the container if shown or null.
      */
-    public static PopupContainerWithArrow showForIcon(BubbleTextView icon) {
+    public static PopupContainerWithArrow<Launcher> showForIcon(BubbleTextView icon) {
         Launcher launcher = Launcher.getLauncher(icon.getContext());
         if (getOpen(launcher) != null) {
             // There is already an items container open, so don't open this one.
@@ -205,7 +206,7 @@
             return null;
         }
 
-        final PopupContainerWithArrow container =
+        final PopupContainerWithArrow<Launcher> container =
                 (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
                         R.layout.popup_container, launcher.getDragLayer(), false);
         container.configureForLauncher(launcher);
@@ -489,8 +490,8 @@
     /**
      * Returns a PopupContainerWithArrow which is already open or null
      */
-    public static PopupContainerWithArrow getOpen(BaseDraggingActivity launcher) {
-        return getOpenView(launcher, TYPE_ACTION_POPUP);
+    public static <T extends Context & ActivityContext> PopupContainerWithArrow getOpen(T context) {
+        return getOpenView(context, TYPE_ACTION_POPUP);
     }
 
     /**
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 5ed6f2e..1dce1f2 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -26,7 +27,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.ItemInfo;
@@ -36,6 +36,7 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -128,7 +129,8 @@
     /**
      * Returns a runnable to update the provided shortcuts and notifications
      */
-    public static Runnable createUpdateRunnable(final BaseDraggingActivity launcher,
+    public static <T extends Context & ActivityContext> Runnable createUpdateRunnable(
+            final T context,
             final ItemInfo originalInfo,
             final Handler uiHandler, final PopupContainerWithArrow container,
             final List<DeepShortcutView> shortcutViews,
@@ -144,22 +146,22 @@
                     infos = Collections.emptyList();
                 } else {
                     infos = notificationListener.getNotificationsForKeys(notificationKeys).stream()
-                            .map(sbn -> new NotificationInfo(launcher, sbn, originalInfo))
+                            .map(sbn -> new NotificationInfo(context, sbn, originalInfo))
                             .collect(Collectors.toList());
                 }
                 uiHandler.post(() -> container.applyNotificationInfos(infos));
             }
 
-            List<ShortcutInfo> shortcuts = new ShortcutRequest(launcher, user)
+            List<ShortcutInfo> shortcuts = new ShortcutRequest(context, user)
                     .withContainer(activity)
                     .query(ShortcutRequest.PUBLISHED);
             String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
                     : notificationKeys.get(0).shortcutId;
             shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
-            IconCache cache = LauncherAppState.getInstance(launcher).getIconCache();
+            IconCache cache = LauncherAppState.getInstance(context).getIconCache();
             for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
                 final ShortcutInfo shortcut = shortcuts.get(i);
-                final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
+                final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, context);
                 cache.getUnbadgedShortcutIcon(si, shortcut);
                 si.rank = i;
                 si.container = CONTAINER_SHORTCUTS;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index e5424cf..826c79b 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -18,12 +18,14 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.WidgetsBottomSheet;
 
 import java.util.List;
@@ -35,7 +37,7 @@
  * Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
  * @param <T>
  */
-public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo
+public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
         implements View.OnClickListener {
 
     private final int mIconResId;
@@ -100,7 +102,7 @@
         return mAccessibilityActionId == action;
     }
 
-    public interface Factory<T extends BaseDraggingActivity> {
+    public interface Factory<T extends Context & ActivityContext> {
 
         @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
     }
@@ -135,9 +137,9 @@
 
     public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
 
-    public static class AppInfo extends SystemShortcut {
+    public static class AppInfo<T extends Context & ActivityContext> extends SystemShortcut<T> {
 
-        public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
+        public AppInfo(T target, ItemInfo itemInfo) {
             super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
                     itemInfo);
         }
@@ -145,7 +147,7 @@
         @Override
         public void onClick(View view) {
             dismissTaskMenuView(mTarget);
-            Rect sourceBounds = mTarget.getViewBounds(view);
+            Rect sourceBounds = Utilities.getViewBounds(view);
             new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
@@ -170,7 +172,7 @@
         return new Install(activity, itemInfo);
     };
 
-    public static class Install extends SystemShortcut {
+    public static class Install extends SystemShortcut<BaseDraggingActivity> {
 
         public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
             super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
@@ -186,7 +188,7 @@
         }
     }
 
-    public static void dismissTaskMenuView(BaseDraggingActivity activity) {
+    public static <T extends Context & ActivityContext> void dismissTaskMenuView(T activity) {
         AbstractFloatingView.closeOpenViews(activity, true,
             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
     }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 893a215..5bf0342 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -104,6 +104,8 @@
     public static final String REQUEST_GET_ACTIVITIES = "get-activities";
     public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET =
             "get-focused-task-height-for-tablet";
+    public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET =
+            "get-grid-task-size-rect-for-tablet";
 
     public static Long sForcePauseTimeout;
     public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout";
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 5f8a4d4..61d488c 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
@@ -217,7 +216,7 @@
                     mFlingBlockCheck.blockFling();
                 }
             }
-            if (mToState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+            if (mToState == LauncherState.ALL_APPS) {
                 mAllAppsOvershootStarted = true;
                 // 1f, value when all apps container hit the top
                 mLauncher.getAppsView().onPull(progress - 1f, progress - 1f);
@@ -333,7 +332,7 @@
         anim.setFloatValues(startProgress, endProgress);
         updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStart();
-        if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+        if (targetState == LauncherState.ALL_APPS) {
             if (mAllAppsOvershootStarted) {
                 mLauncher.getAppsView().onRelease();
                 mAllAppsOvershootStarted = false;
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
index bfdf1e4..e1b9478 100644
--- a/src/com/android/launcher3/util/WindowManagerCompat.java
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -60,10 +60,10 @@
         }
         WindowInsets defaultInsets = windowContext.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics().getWindowInsets();
-        boolean hasNavbar = ResourceUtils.getIntegerByName(
+        boolean isGesturalMode = ResourceUtils.getIntegerByName(
                 "config_navBarInteractionMode",
                 windowContext.getResources(),
-                INVALID_RESOURCE_HANDLE) != 0;
+                INVALID_RESOURCE_HANDLE) == 2;
 
         WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(defaultInsets);
         Set<WindowBounds> result = new ArraySet<>();
@@ -74,7 +74,7 @@
         if (isTablet && !consumeTaskBar) {
             portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
                     .getDimensionPixelSize(R.dimen.taskbar_size));
-        } else if (hasNavbar) {
+        } else if (!isGesturalMode) {
             portraitNav = Insets.of(0, 0, 0,
                     getSystemResource(windowContext, "navigation_bar_height", swDP));
             landscapeNav = isTablet
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index e07d71e..a2e4ad6 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -19,6 +19,7 @@
 import android.content.ContextWrapper;
 import android.graphics.Rect;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.View.AccessibilityDelegate;
 
 import androidx.annotation.Nullable;
@@ -159,4 +160,10 @@
             return null;
         }
     }
+
+    default View.OnClickListener getItemOnClickListener() {
+        return v -> {
+            // No op.
+        };
+    }
 }
diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java
index 42701c6..1cb27e1 100644
--- a/src/com/android/launcher3/views/BubbleTextHolder.java
+++ b/src/com/android/launcher3/views/BubbleTextHolder.java
@@ -16,14 +16,13 @@
 package com.android.launcher3.views;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 
 /**
  * Views that contain {@link BubbleTextView} should implement this interface.
  */
-public interface BubbleTextHolder extends IconCache.ItemInfoUpdateReceiver {
+public interface BubbleTextHolder {
     BubbleTextView getBubbleText();
 
     /**
@@ -31,6 +30,6 @@
      *
      * @param itemInfo the new itemInfo
      */
-    @Override
-    default void reapplyItemInfo(ItemInfoWithIcon itemInfo){};
+    default void onItemInfoUpdated(ItemInfoWithIcon itemInfo) {
+    }
 }
diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
deleted file mode 100644
index a434d07..0000000
--- a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.plugins;
-
-import android.view.MotionEvent;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this interface to receive a callback when the user swipes right
- * to left on the gesture area. It won't fire if the user has quick switched to a previous app
- * (swiped right) and the current app isn't yet the active one (i.e., if swiping left would take
- * the user to a more recent app).
- */
-@ProvidesInterface(action = com.android.systemui.plugins.OverscrollPlugin.ACTION,
-        version = com.android.systemui.plugins.OverscrollPlugin.VERSION)
-public interface OverscrollPlugin extends Plugin {
-
-    String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
-    int VERSION = 4;
-
-    String DEVICE_STATE_LOCKED = "Locked";
-    String DEVICE_STATE_LAUNCHER = "Launcher";
-    String DEVICE_STATE_APP = "App";
-    String DEVICE_STATE_UNKNOWN = "Unknown";
-
-    /**
-     * @return true if the plugin is active and will accept overscroll gestures
-     */
-    boolean isActive();
-
-    /**
-     * Called when a touch has been recognized as an overscroll gesture.
-     * @param horizontalDistancePx Horizontal distance from the last finger location to the finger
-     *                               location when it first touched the screen.
-     * @param verticalDistancePx Horizontal distance from the last finger location to the finger
-     *                             location when it first touched the screen.
-     * @param thresholdPx Minimum distance for gesture.
-     * @param flingDistanceThresholdPx Minimum distance for gesture by fling.
-     * @param flingVelocityThresholdPx Minimum velocity for gesture by fling.
-     * @param deviceState String representing the current device state
-     * @param underlyingActivity String representing the currently active Activity
-     */
-    void onTouchEvent(MotionEvent event,
-                      int horizontalDistancePx,
-                      int verticalDistancePx,
-                      int thresholdPx,
-                      int flingDistanceThresholdPx,
-                      int flingVelocityThresholdPx,
-                      String deviceState,
-                      String underlyingActivity);
-
-    /**
-     * @return `true` if overscroll gesture handling should override all other gestures.
-     */
-    boolean blockOtherGestures();
-
-    /**
-     * @return `true` if the overscroll gesture can pan the underlying app.
-     */
-    boolean allowsUnderlyingActivityOverscroll();
-}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 8222f75..aae8fb5 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -145,6 +145,16 @@
             <meta-data android:name="android.app.shortcuts"
                        android:resource="@xml/shortcuts"/>
         </activity>
+        <activity
+            android:name="com.android.launcher3.testcomponent.OtherBaseTestingActivity"
+            android:label="OtherLauncherTestApp"
+            android:exported="true"
+            android:taskAffinity="com.android.launcher3.testcomponent.Affinity2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
         <activity-alias android:name="Activity2"
                         android:label="TestActivity2"
                         android:exported="true"
@@ -208,31 +218,36 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
-        <activity-alias android:name="Activity9"
-                        android:label="TestActivity9"
-                        android:exported="true"
-                        android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+        <activity-alias android:name="Activity9" android:exported="true"
+            android:label="TestActivity9"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
-        <activity-alias android:name="Activity10"
-                        android:label="TestActivity10"
-                        android:exported="true"
-                        android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+        <activity-alias android:name="Activity10" android:exported="true"
+            android:label="TestActivity10"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
-        <activity-alias android:name="Activity11"
-                        android:label="TestActivity11"
-                        android:exported="true"
-                        android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+        <activity-alias android:name="Activity11" android:exported="true"
+            android:label="TestActivity11"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
+        <activity-alias android:name="Activity12" android:exported="true"
+            android:label="TestActivity12"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
     </application>
diff --git a/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java
new file mode 100644
index 0000000..8bcab62
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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.testcomponent;
+
+/**
+ * Extension of BaseTestingActivity to help test many activities open at once.
+ */
+public class OtherBaseTestingActivity extends BaseTestingActivity {}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 5ea5d65..2c9785c 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -15,28 +15,24 @@
  */
 package com.android.launcher3.ui.widget;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
 
 import android.appwidget.AppWidgetManager;
 import android.content.Intent;
-import android.view.View;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.tapl.Widget;
+import com.android.launcher3.tapl.WidgetResizeFrame;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.testcomponent.WidgetConfigActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.Wait.Condition;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
@@ -92,48 +88,26 @@
 
         // Drag widget to homescreen
         WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
-        widgets.getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
-                .dragToWorkspace(true, false);
+        WidgetResizeFrame resizeFrame =
+                widgets.getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
+                        .dragConfigWidgetToWorkspace(acceptConfig);
         // Widget id for which the config activity was opened
         mWidgetId = monitor.getWidgetId();
 
         // Verify that the widget id is valid and bound
         assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
 
-        setResult(acceptConfig);
         if (acceptConfig) {
-            // TODO(b/192655785) Assert widget resize frame is shown and then dismiss it.
-            Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
-            assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
+            assertNotNull("Widget resize frame not shown after widget added", resizeFrame);
+            resizeFrame.dismiss();
+
+            final Widget widget =
+                    mLauncher.getWorkspace().tryGetWidget(mWidgetInfo.label, DEFAULT_UI_TIMEOUT);
+            assertNotNull("Widget not found on the workspace", widget);
         } else {
-            // Verify that the widget id is deleted.
-            Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
-                    DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
-        }
-    }
-
-    private void setResult(boolean success) {
-        getInstrumentation().getTargetContext().sendBroadcast(
-                WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
-                        success ? "clickOK" : "clickCancel"));
-    }
-
-    /**
-     * Condition for searching widget id
-     */
-    private class WidgetSearchCondition implements Condition, ItemOperator {
-
-        @Override
-        public boolean isTrue() throws Throwable {
-            return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get();
-        }
-
-        @Override
-        public boolean evaluate(ItemInfo info, View view) {
-            return info instanceof LauncherAppWidgetInfo &&
-                    ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
-                            mWidgetInfo.provider.getClassName()) &&
-                    ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
+            final Widget widget =
+                    mLauncher.getWorkspace().tryGetWidget(mWidgetInfo.label, DEFAULT_UI_TIMEOUT);
+            assertNull("Widget unexpectedly found on the workspace", widget);
         }
     }
 
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index dad4f2b..194ee4f 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -25,6 +25,7 @@
 
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Widget;
+import com.android.launcher3.tapl.WidgetResizeFrame;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
@@ -53,19 +54,20 @@
         final LauncherAppWidgetProviderInfo widgetInfo =
                 TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
 
-        mLauncher.
+        WidgetResizeFrame resizeFrame = mLauncher.
                 getWorkspace().
                 openAllWidgets().
                 getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
-                dragToWorkspace(false, false);
-        // Dismiss widget resize frame.
-        mDevice.pressHome();
+                dragWidgetToWorkspace();
 
         assertTrue(mActivityMonitor.itemExists(
                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
                         ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
                                 widgetInfo.provider.getClassName())).call());
 
+        assertNotNull("Widget resize frame not shown after widget add", resizeFrame);
+        resizeFrame.dismiss();
+
         final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
                 DEFAULT_UI_TIMEOUT);
         assertNotNull("Widget not found on the workspace", widget);
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index b037be4..0e3b501 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -25,6 +25,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Common overview panel for both Launcher and fallback recents
@@ -53,14 +54,18 @@
     }
 
     private void flingForwardImpl() {
+        flingForwardImpl(0);
+    }
+
+    private void flingForwardImpl(int rightMargin) {
         try (LauncherInstrumentation.Closable c =
                      mLauncher.addContextLayer("want to fling forward in overview")) {
             LauncherInstrumentation.log("Overview.flingForward before fling");
             final UiObject2 overview = verifyActiveContainer();
             final int leftMargin =
                     mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth();
-            mLauncher.scroll(
-                    overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, false);
+            mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, rightMargin, 0),
+                    20, false);
             try (LauncherInstrumentation.Closable c2 =
                          mLauncher.addContextLayer("flung forwards")) {
                 verifyActiveContainer();
@@ -86,6 +91,8 @@
 
             mLauncher.clickLauncherObject(
                     mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector));
+
+            mLauncher.waitUntilLauncherObjectGone(clearAllSelector);
         }
     }
 
@@ -111,6 +118,40 @@
     }
 
     /**
+     * Scrolls the current task via flinging forward until it is off screen.
+     *
+     * If only one task is present, it is only partially scrolled off screen and will still be
+     * the current task.
+     */
+    public void scrollCurrentTaskOffScreen() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to scroll current task off screen in overview")) {
+            verifyActiveContainer();
+
+            OverviewTask task = getCurrentTask();
+            mLauncher.assertNotNull("current task is null", task);
+            flingForwardImpl(task.getTaskCenterX());
+
+            try (LauncherInstrumentation.Closable c2 =
+                         mLauncher.addContextLayer("scrolled task off screen")) {
+                verifyActiveContainer();
+                verifyActionsViewVisibility();
+
+                if (getTaskCount() > 1) {
+                    if (mLauncher.isTablet()) {
+                        mLauncher.assertTrue("current task is not grid height",
+                                getCurrentTask().getVisibleHeight() == mLauncher
+                                        .getGridTaskRectForTablet().height());
+                    }
+                    mLauncher.assertTrue("Current task not scrolled off screen",
+                            !getCurrentTask().equals(task));
+                }
+            }
+        }
+    }
+
+    /**
      * Gets the current task in the carousel, or fails if the carousel is empty.
      *
      * @return the task in the middle of the visible tasks list.
@@ -130,6 +171,20 @@
         return new OverviewTask(mLauncher, widestTask, this);
     }
 
+    /**
+     * Returns a list of all tasks fully visible in the tablet grid overview.
+     */
+    @NonNull
+    public List<OverviewTask> getCurrentTasksForTablet() {
+        final List<UiObject2> taskViews = getTasks();
+        mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
+
+        final int gridTaskWidth = mLauncher.getGridTaskRectForTablet().width();
+
+        return taskViews.stream().filter(t -> t.getVisibleBounds().width() == gridTaskWidth).map(
+                t -> new OverviewTask(mLauncher, t, this)).collect(Collectors.toList());
+    }
+
     @NonNull
     private List<UiObject2> getTasks() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -166,11 +221,18 @@
         }
     }
 
+    /**
+     * Returns if clear all button is visible.
+     */
+    public boolean isClearAllVisible() {
+        return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
+    }
+
     /* TODO(b/197630182): Once b/188790554 is fixed, remove instanceof check. Currently, when
         swiping from app to overview in Fallback Recents, taskbar remains and no action buttons
         are visible, so we are only testing Overview for now, not BaseOverview. */
     private void verifyActionsViewVisibility() {
-        if (!(this instanceof Overview)) {
+        if (!(this instanceof Overview) || !hasTasks()) {
             return;
         }
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -198,8 +260,13 @@
 
     /**
      * Returns Overview focused task if it exists.
+     *
+     * @throws IllegalStateException if not run on a tablet device.
      */
     UiObject2 getFocusedTaskForTablet() {
+        if (!mLauncher.isTablet()) {
+            throw new IllegalStateException("Must be run on tablet device.");
+        }
         final List<UiObject2> taskViews = getTasks();
         if (taskViews.size() == 0) {
             return null;
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 55fb2c1..9ee0e25 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -326,6 +326,11 @@
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    Rect getGridTaskRectForTablet() {
+        return ((Rect) getTestInfo(TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET)
+                .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD));
+    }
+
     float getExactScreenCenterX() {
         return getRealDisplaySize().x / 2f;
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 9419839..15bddd7 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -24,7 +24,9 @@
 
 import com.android.launcher3.testing.TestProtocol;
 
+import java.util.List;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * A recent task in the overview panel carousel.
@@ -47,10 +49,14 @@
         mOverview.verifyActiveContainer();
     }
 
-    private int getVisibleHeight() {
+    int getVisibleHeight() {
         return mTask.getVisibleBounds().height();
     }
 
+    int getTaskCenterX() {
+        return mTask.getVisibleCenter().x;
+    }
+
     /**
      * Dismisses the task by swiping up.
      */
@@ -68,6 +74,8 @@
 
             boolean taskWasFocused = mLauncher.isTablet() && getVisibleHeight() == mLauncher
                     .getFocusedTaskHeightForTablet();
+            List<Integer> originalTasksCenterX = getCurrentTasksCenterXList();
+            boolean isClearAllVisibleBeforeDismiss = mOverview.isClearAllVisible();
 
             dismissBySwipingUp();
 
@@ -76,6 +84,16 @@
                     mLauncher.assertNotNull("No task became focused",
                             mOverview.getFocusedTaskForTablet());
                 }
+                if (!isClearAllVisibleBeforeDismiss) {
+                    List<Integer> currentTasksCenterX = getCurrentTasksCenterXList();
+                    if (originalTasksCenterX.size() == currentTasksCenterX.size()) {
+                        // Check for the same number of visible tasks before and after to
+                        // avoid asserting on cases of shifting all tasks to close the distance
+                        // between clear all and tasks at the end of the grid.
+                        mLauncher.assertTrue("Task centers not aligned",
+                                originalTasksCenterX.equals(currentTasksCenterX));
+                    }
+                }
             }
         }
     }
@@ -94,6 +112,14 @@
                         + centerY, "swiping to dismiss");
     }
 
+    private List<Integer> getCurrentTasksCenterXList() {
+        return mLauncher.isTablet()
+                ? mOverview.getCurrentTasksForTablet().stream()
+                    .map(OverviewTask::getTaskCenterX)
+                    .collect(Collectors.toList())
+                : List.of(mOverview.getCurrentTask().getTaskCenterX());
+    }
+
     /**
      * Clicks at the task.
      */
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 3520318..f569ef4 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -16,7 +16,12 @@
 
 package com.android.launcher3.tapl;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import com.android.launcher3.testing.TestProtocol;
 
@@ -51,4 +56,55 @@
     protected String launchableType() {
         return "widget";
     }
+
+    /**
+     * Drags a non-configurable widget from the widgets container to the workspace and returns the
+     * resize frame that is shown after the widget is added.
+     */
+    @NonNull
+    public WidgetResizeFrame dragWidgetToWorkspace() {
+        return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false);
+    }
+
+    /**
+     * Drags a configurable widget from the widgets container to the workspace, either accepts or
+     * cancels the configuration based on {@code acceptsConfig}, and returns the resize frame that
+     * is shown if the widget is added.
+     */
+    @Nullable
+    public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) {
+        return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig);
+    }
+
+    /**
+     * Drags a widget from the widgets container to the workspace and returns the resize frame that
+     * is shown after the widget is added.
+     *
+     * <p> If {@code configurable} is true, then either accepts or cancels the configuration based
+     * on {@code acceptsConfig}.
+     */
+    @Nullable
+    private WidgetResizeFrame dragWidgetToWorkspace(
+            boolean configurable, boolean acceptsConfig) {
+        dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false);
+
+        if (configurable) {
+            // Configure the widget.
+            BySelector selector = By.text(acceptsConfig ? "OK" : "Cancel");
+            mLauncher.getDevice()
+                    .wait(Until.findObject(selector), LauncherInstrumentation.WAIT_TIME_MS)
+                    .click();
+
+            // If the widget configuration was cancelled, then the widget wasn't added to the home
+            // screen. In that case, we cannot return a resize frame.
+            if (!acceptsConfig) {
+                return null;
+            }
+        }
+
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get widget resize frame")) {
+            return new WidgetResizeFrame(mLauncher);
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
new file mode 100644
index 0000000..8f51d04
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
@@ -0,0 +1,37 @@
+/*
+ * 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.tapl;
+
+/** The resize frame that is shown for a widget on the workspace. */
+public class WidgetResizeFrame {
+
+    private final LauncherInstrumentation mLauncher;
+
+    WidgetResizeFrame(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+        launcher.waitForLauncherObject("widget_resize_frame");
+    }
+
+    /** Dismisses the resize frame. */
+    public void dismiss() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to dismiss widget resize frame")) {
+            // Dismiss the resize frame by pressing the home button.
+            mLauncher.getDevice().pressHome();
+        }
+    }
+}