Merge "Do not pip when ending live tile in Overview" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1fad72d..8066816 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -49,7 +49,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
diff --git a/go/AndroidManifest-launcher.xml b/go/AndroidManifest-launcher.xml
index d2575b6..6a8f715 100644
--- a/go/AndroidManifest-launcher.xml
+++ b/go/AndroidManifest-launcher.xml
@@ -49,7 +49,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|uiMode"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density|uiMode"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
diff --git a/go/quickstep/res/drawable/ic_listen.xml b/go/quickstep/res/drawable/ic_listen.xml
index a8e6c93..16bb597 100644
--- a/go/quickstep/res/drawable/ic_listen.xml
+++ b/go/quickstep/res/drawable/ic_listen.xml
@@ -13,20 +13,12 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="28dp"
-    android:height="28dp"
-    android:viewportWidth="28"
-    android:viewportHeight="28">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
   <path
-      android:pathData="M10.5,15.17c2.58,0 4.67,-2.09 4.67,-4.67s-2.09,-4.67 -4.67,-4.67c-2.58,0 -4.67,2.09 -4.67,4.67S7.92,15.17 10.5,15.17zM10.5,8.17c1.28,0 2.33,1.05 2.33,2.33s-1.05,2.33 -2.33,2.33c-1.28,0 -2.33,-1.05 -2.33,-2.33S9.22,8.17 10.5,8.17z"
-      android:fillColor="#4285F4"/>
-  <path
-      android:pathData="M10.5,17.5c-3.11,0 -9.33,1.56 -9.33,4.67v2.33h18.67v-2.33C19.83,19.06 13.62,17.5 10.5,17.5zM3.5,22.17c0.26,-0.84 3.86,-2.33 7,-2.33c3.15,0 6.77,1.5 7,2.33H3.5z"
-      android:fillColor="#4285F4"/>
-  <path
-      android:pathData="M25.67,10.5c0,0.36 -0.02,0.71 -0.05,1.05c-0.01,0.15 -0.03,0.29 -0.05,0.43c-0.02,0.18 -0.05,0.36 -0.08,0.54c-0.04,0.2 -0.07,0.39 -0.12,0.58c-0.01,0.06 -0.03,0.11 -0.04,0.17c-0.59,2.34 -1.81,4.01 -2.52,4.82c-0.09,0.1 -0.18,0.2 -0.28,0.3c-0.17,0.18 -0.27,0.27 -0.27,0.27l-1.65,-1.63c1.34,-1.33 2.27,-3.07 2.6,-5.01c0.01,-0.08 0.02,-0.16 0.04,-0.24c0.06,-0.42 0.1,-0.85 0.1,-1.29c0,-0.44 -0.04,-0.88 -0.1,-1.3c-0.01,-0.06 -0.02,-0.13 -0.03,-0.19c-0.32,-1.95 -1.25,-3.7 -2.6,-5.04l1.65,-1.63c0,0 0.11,0.1 0.27,0.27c0.09,0.1 0.19,0.2 0.28,0.3c0.71,0.82 1.93,2.48 2.52,4.82c0.01,0.06 0.03,0.11 0.04,0.17c0.04,0.19 0.08,0.38 0.12,0.58c0.03,0.18 0.06,0.36 0.08,0.54c0.02,0.14 0.04,0.28 0.05,0.43C25.65,9.79 25.67,10.14 25.67,10.5z"
-      android:fillColor="#EA4335"/>
-  <path
-      android:pathData="M20.61,8.4C20.85,9.06 21,9.76 21,10.5s-0.15,1.44 -0.39,2.1c-0.28,0.77 -0.71,1.46 -1.25,2.05l-1.66,-1.64c0.56,-0.63 0.91,-1.44 0.95,-2.34c0,-0.06 0.02,-0.11 0.02,-0.17s-0.01,-0.11 -0.02,-0.17c-0.04,-0.9 -0.39,-1.71 -0.95,-2.34l1.66,-1.64C19.9,6.94 20.32,7.63 20.61,8.4z"
-      android:fillColor="#FBBC04"/>
+      android:fillColor="@android:color/white"
+      android:pathData="M15.08,7.05c0.84,1.18 0.84,2.71 0,3.89l1.68,1.69c2.02,-2.02 2.02,-5.07 0,-7.27l-1.68,1.69zM20.07,2l-1.63,1.63c2.77,3.02 2.77,7.56 0,10.74L20.07,16c3.9,-3.89 3.91,-9.95 0,-14zM9,13c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM9,7c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zM9,14c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4zM15,19L3,19v-0.99C3.2,17.29 6.3,16 9,16s5.8,1.29 6,2v1z"/>
 </vector>
diff --git a/go/quickstep/res/drawable/ic_search.xml b/go/quickstep/res/drawable/ic_search.xml
index 4307330..ce290d9 100644
--- a/go/quickstep/res/drawable/ic_search.xml
+++ b/go/quickstep/res/drawable/ic_search.xml
@@ -13,20 +13,24 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="28dp"
-    android:height="28dp"
-    android:viewportWidth="28"
-    android:viewportHeight="28">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
   <path
-      android:pathData="M24.5,22.75l-6.84,-6.84c1,-1.35 1.59,-3.02 1.59,-4.83h-2.33c0,3.22 -2.62,5.83 -5.83,5.83v2.33c1.81,0 3.47,-0.6 4.83,-1.59l6.84,6.84L24.5,22.75z"
-      android:fillColor="#4285F4"/>
+      android:fillColor="@android:color/white"
+      android:pathData="M16,3h-2v2h2c1.65,0 3,1.35 3,3v2h2V8C21,5.24 18.76,3 16,3z"/>
   <path
-      android:pathData="M11.08,2.92v2.33c3.22,0 5.83,2.62 5.83,5.83h2.33C19.25,6.57 15.59,2.92 11.08,2.92z"
-      android:fillColor="#34A853"/>
+      android:fillColor="@android:color/white"
+      android:pathData="M5,16v-2H3v2c0,2.76 2.24,5 5,5h2v-2H8C6.35,19 5,17.65 5,16z"/>
   <path
-      android:pathData="M5.25,11.08H2.92c0,4.51 3.66,8.17 8.17,8.17v-2.33C7.87,16.92 5.25,14.3 5.25,11.08z"
-      android:fillColor="#EA4335"/>
+      android:fillColor="@android:color/white"
+      android:pathData="M12,8.5c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S13.93,8.5 12,8.5z"/>
   <path
-      android:pathData="M2.92,11.08h2.33c0,-3.22 2.62,-5.83 5.83,-5.83V2.92C6.57,2.92 2.92,6.57 2.92,11.08z"
-      android:fillColor="#FBBC04"/>
+      android:fillColor="@android:color/white"
+      android:pathData="M18,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M5,8c0,-1.65 1.35,-3 3,-3h2V3H8C5.24,3 3,5.24 3,8v2h2V8z"/>
 </vector>
diff --git a/go/quickstep/res/drawable/ic_translate.xml b/go/quickstep/res/drawable/ic_translate.xml
index 1247807..e3df56f 100644
--- a/go/quickstep/res/drawable/ic_translate.xml
+++ b/go/quickstep/res/drawable/ic_translate.xml
@@ -13,20 +13,12 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="28dp"
-    android:height="28dp"
-    android:viewportWidth="28"
-    android:viewportHeight="28">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
   <path
-      android:pathData="M12.28,15.19l-0.07,-0.05c-0.61,-0.49 -1.15,-1.05 -1.65,-1.63c-1.05,-1.22 -1.88,-2.63 -2.39,-4.17H5.83c0.54,2.17 1.58,4.16 3.01,5.85l-5.93,5.23l1.75,1.75l5.91,-5.26c0.05,0.04 0.1,0.09 0.15,0.13l3.42,2.79l1.02,-2.33L12.28,15.19z"
-      android:fillColor="#FBBC04"/>
-  <path
-      android:pathData="M21.58,11.67h-2.33l-5.25,14h2.33l1.31,-3.5h5.54l1.32,3.5h2.33L21.58,11.67zM18.53,19.83l1.89,-5.05l1.89,5.05H18.53z"
-      android:fillColor="#4285F4"/>
-  <path
-      android:pathData="M11.67,2.33l-2.34,0l0,2.34l-8.16,0l0,2.33l10.5,0l0,-2.33z"
-      android:fillColor="#EA4335"/>
-  <path
-      android:pathData="M11.67,4.67V7H14c-0.61,2.42 -1.79,4.65 -3.44,6.5c0.5,0.59 1.04,1.15 1.65,1.63l0.07,0.05c2.03,-2.32 3.44,-5.14 4.05,-8.19h3.5V4.67H11.67z"
-      android:fillColor="#34A853"/>
+      android:fillColor="@android:color/white"
+      android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
 </vector>
diff --git a/go/quickstep/res/drawable/round_rect_button.xml b/go/quickstep/res/drawable/round_rect_button.xml
new file mode 100644
index 0000000..b276686
--- /dev/null
+++ b/go/quickstep/res/drawable/round_rect_button.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:radius="@dimen/go_button_corner_radius" />
+</shape>
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index 4aa7774..6a331ea 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -25,7 +25,7 @@
         android:id="@+id/action_buttons"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_gravity="top|center_horizontal"
         android:orientation="horizontal">
 
         <Space
@@ -33,62 +33,75 @@
             android:layout_height="1dp"
             android:layout_weight="1" />
 
-        <Button
-            android:id="@+id/action_translate"
-            style="@style/GoOverviewActionButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:drawableTop="@drawable/ic_translate"
-            android:drawablePadding="1dp"
-            android:text="@string/action_translate"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+        <LinearLayout
+            style="@style/GoOverviewActionButtonContainer">
+            <ImageButton
+                android:id="@+id/action_translate"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_translate"
+                android:contentDescription="@string/action_translate" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_translate" />
+        </LinearLayout>
 
         <Space
             android:layout_width="0dp"
             android:layout_height="1dp"
             android:layout_weight="1" />
 
-        <Button
-            android:id="@+id/action_listen"
-            style="@style/GoOverviewActionButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:drawableTop="@drawable/ic_listen"
-            android:drawablePadding="1dp"
-            android:text="@string/action_listen"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+        <LinearLayout
+            style="@style/GoOverviewActionButtonContainer">
+            <ImageButton
+                android:id="@+id/action_listen"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_listen"
+                android:contentDescription="@string/action_listen"
+                android:background="@drawable/round_rect_button" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_listen" />
+        </LinearLayout>
 
         <Space
             android:layout_width="0dp"
             android:layout_height="1dp"
             android:layout_weight="1" />
 
-        <Button
-            android:id="@+id/action_screenshot"
-            style="@style/GoOverviewActionButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:drawableTop="@drawable/ic_screenshot"
-            android:drawablePadding="1dp"
-            android:text="@string/action_screenshot"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+        <LinearLayout
+            style="@style/GoOverviewActionButtonContainer">
+            <ImageButton
+                android:id="@+id/action_screenshot"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_screenshot"
+                android:contentDescription="@string/action_screenshot"
+                android:background="@drawable/round_rect_button" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_screenshot" />
+        </LinearLayout>
 
         <Space
             android:layout_width="0dp"
             android:layout_height="1dp"
             android:layout_weight="1" />
 
-        <Button
-            android:id="@+id/action_search"
-            style="@style/GoOverviewActionButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:drawableTop="@drawable/ic_search"
-            android:drawablePadding="1dp"
-            android:text="@string/action_search"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor"
-            android:visibility="gone" />
+        <!-- Will be enabled in a future version. -->
+        <LinearLayout
+            style="@style/GoOverviewActionButtonContainer"
+            android:visibility="gone" >
+            <ImageButton
+                android:id="@+id/action_search"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_search"
+                android:contentDescription="@string/action_search"
+                android:background="@drawable/round_rect_button" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_search" />
+        </LinearLayout>
 
+        <!-- Unused. Included only for compatibility with parent class. -->
         <Button
             android:id="@+id/action_share"
             style="@style/GoOverviewActionButton"
@@ -98,13 +111,6 @@
             android:text="@string/action_share"
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
-
-        <Space
-            android:id="@+id/oav_three_button_space"
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="1"
-            android:visibility="gone" />
     </LinearLayout>
 
 </com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/go/quickstep/res/values-af/strings.xml b/go/quickstep/res/values-af/strings.xml
new file mode 100644
index 0000000..8570531
--- /dev/null
+++ b/go/quickstep/res/values-af/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Deel program"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Luister"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Vertaal"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-da/strings.xml b/go/quickstep/res/values-da/strings.xml
new file mode 100644
index 0000000..46648ce
--- /dev/null
+++ b/go/quickstep/res/values-da/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Del app"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Lyt"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Oversæt"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-es-rUS/strings.xml b/go/quickstep/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..e6946d7
--- /dev/null
+++ b/go/quickstep/res/values-es-rUS/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Compartir app"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Escuchar"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Traducir"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-fa/strings.xml b/go/quickstep/res/values-fa/strings.xml
new file mode 100644
index 0000000..491b32f
--- /dev/null
+++ b/go/quickstep/res/values-fa/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"هم‌رسانی برنامه"</string>
+    <string name="action_listen" msgid="2370304050784689486">"گوش دادن"</string>
+    <string name="action_translate" msgid="8028378961867277746">"ترجمه"</string>
+    <string name="action_search" msgid="6269564710943755464">"لنز"</string>
+</resources>
diff --git a/go/quickstep/res/values-kn/strings.xml b/go/quickstep/res/values-kn/strings.xml
new file mode 100644
index 0000000..1083e84
--- /dev/null
+++ b/go/quickstep/res/values-kn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"ಆ್ಯಪ್ ಹಂಚಿಕೊಳ್ಳಿ"</string>
+    <string name="action_listen" msgid="2370304050784689486">"ಆಲಿಸಿ"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Translate"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-ms/strings.xml b/go/quickstep/res/values-ms/strings.xml
new file mode 100644
index 0000000..39698e8
--- /dev/null
+++ b/go/quickstep/res/values-ms/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Kongsi Apl"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Dengar"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Terjemah"</string>
+    <string name="action_search" msgid="6269564710943755464">"Kanta"</string>
+</resources>
diff --git a/go/quickstep/res/values-nl/strings.xml b/go/quickstep/res/values-nl/strings.xml
new file mode 100644
index 0000000..ff5f16b
--- /dev/null
+++ b/go/quickstep/res/values-nl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"App delen"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Luisteren"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Vertalen"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-pl/strings.xml b/go/quickstep/res/values-pl/strings.xml
new file mode 100644
index 0000000..a9696a6
--- /dev/null
+++ b/go/quickstep/res/values-pl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Udostępnij aplikację"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Posłuchaj"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Przetłumacz"</string>
+    <string name="action_search" msgid="6269564710943755464">"Obiektyw"</string>
+</resources>
diff --git a/go/quickstep/res/values-pt/strings.xml b/go/quickstep/res/values-pt/strings.xml
new file mode 100644
index 0000000..6be2219
--- /dev/null
+++ b/go/quickstep/res/values-pt/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Compartilhar app"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Ouvir"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Traduzir"</string>
+    <string name="action_search" msgid="6269564710943755464">"Google Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-sk/strings.xml b/go/quickstep/res/values-sk/strings.xml
new file mode 100644
index 0000000..f142492
--- /dev/null
+++ b/go/quickstep/res/values-sk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Zdieľať aplikáciu"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Počúvať"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Preložiť"</string>
+    <string name="action_search" msgid="6269564710943755464">"Lens"</string>
+</resources>
diff --git a/go/quickstep/res/values-zu/strings.xml b/go/quickstep/res/values-zu/strings.xml
new file mode 100644
index 0000000..6dd94cb
--- /dev/null
+++ b/go/quickstep/res/values-zu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_share_drop_target_label" msgid="5804774105974539508">"Yabelana nge-App"</string>
+    <string name="action_listen" msgid="2370304050784689486">"Lalela"</string>
+    <string name="action_translate" msgid="8028378961867277746">"Humusha"</string>
+    <string name="action_search" msgid="6269564710943755464">"Ilensi"</string>
+</resources>
diff --git a/go/quickstep/res/values/colors.xml b/go/quickstep/res/values/colors.xml
new file mode 100644
index 0000000..9383770
--- /dev/null
+++ b/go/quickstep/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <!-- Overview -->
+    <color name="go_overview_button_icon_color">#3C4043</color>
+    <color name="go_overview_button_color">#70FFFFFF</color>
+</resources>
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
new file mode 100644
index 0000000..cb260b5
--- /dev/null
+++ b/go/quickstep/res/values/dimens.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<resources>
+    <dimen name="go_button_corner_radius">20dp</dimen>
+
+    <!-- Overview -->
+    <dimen name="go_overview_button_width">60dp</dimen>
+    <dimen name="go_overview_button_height">60dp</dimen>
+    <dimen name="go_overview_button_container_width">80dp</dimen>
+    <dimen name="go_overview_button_caption_margin">8dp</dimen>
+</resources>
diff --git a/go/quickstep/res/values/styles.xml b/go/quickstep/res/values/styles.xml
index 927c49a..df49ecd 100644
--- a/go/quickstep/res/values/styles.xml
+++ b/go/quickstep/res/values/styles.xml
@@ -15,12 +15,31 @@
      limitations under the License.
 -->
 <resources>
-    <style name="GoOverviewActionButton"
-        parent="@android:style/Widget.DeviceDefault.Button">
-        <item name="android:textColor">@color/overview_button</item>
-        <item name="android:drawableTint">@color/overview_button</item>
-        <item name="android:tint">?android:attr/textColorPrimary</item>
-        <item name="android:drawablePadding">8dp</item>
-        <item name="android:textAllCaps">false</item>
+    <style name="GoOverviewActionButton">
+        <item name="android:tint">@color/go_overview_button_icon_color</item>
+        <item name="android:backgroundTint">@color/go_overview_button_color</item>
+        <item name="android:background">@drawable/round_rect_button</item>
+        <item name="android:layout_width">@dimen/go_overview_button_width</item>
+        <item name="android:layout_height">@dimen/go_overview_button_height</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="GoOverviewActionButtonCaption">
+        <item name="android:fontFamily">roboto-medium</item>
+        <item name="android:textSize">14dp</item>
+        <item name="android:textColor">@color/go_overview_button_icon_color</item>
+        <item name="android:lineHeight">20dp</item>
+        <item name="android:textAlignment">center</item>
+        <item name="android:importantForAccessibility">no</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">@dimen/go_overview_button_caption_margin</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="GoOverviewActionButtonContainer">
+        <item name="android:layout_width">@dimen/go_overview_button_container_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 7fe9b08..6808222 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -49,7 +49,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 7ab09c5..bf9059f 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -71,7 +71,7 @@
              android:stateNotNeeded="true"
              android:theme="@style/LauncherTheme"
              android:screenOrientation="unspecified"
-             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
+             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
              android:resizeableActivity="true"
              android:resumeWhilePausing="true"
              android:taskAffinity=""/>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 046b3a0..0a82e04 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Probeer weer"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Mooi so!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriaal <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Gereed!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Swiep op om na die tuisskerm toe te gaan"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Jy is gereed om jou foon te begin gebruik"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Navigasie-instellings vir toeganklikheid"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Deel"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 048ced1..f4519b8 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Prøv igen"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Sådan!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Selvstudie <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Alt er parat!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Stryg opad for at gå til startsiden"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Du er klar til at bruge din telefon"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Navigationsindstillinger for hjælpefunktioner"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 2cb0429..10f4479 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reintentar"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"¡Genial!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Instructivo <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Todo listo"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Desliza el dedo hacia arriba para ir a la página principal"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Ya puedes empezar a usar tu teléfono"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Configuración de la navegación para accesibilidad"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"La app o tu organización no permiten realizar esta acción"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index f0bdc4f..bd8f3b8 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"امتحان مجدد"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"عالی!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"آموزش گام‌به‌گام <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"همه چیز آماده است!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"برای رفتن به صفحه اصلی، تند به‌بالا بکشید"</string>
+    <string name="allset_description" msgid="6350320429953234580">"آماده‌اید از تلفنتان استفاده کنید"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"تنظیمات پیمایش برای دسترس‌پذیری"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"هم‌رسانی"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"برنامه یا سازمان شما اجازه نمی‌دهد این کنش انجام شود."</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 1c5f893..3cf37b4 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ಚೆನ್ನಾಗಿದೆ!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"ಟ್ಯುಟೋರಿಯಲ್ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"ಎಲ್ಲವೂ ಸಿದ್ಧವಾಗಿದೆ!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಲು ಮೇಲೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="allset_description" msgid="6350320429953234580">"ನಿಮ್ಮ ಫೋನ್ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ನೀವು ಸಿದ್ದರಾಗಿರುವಿರಿ"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"ಅಕ್ಸೆಸಿಬಿಲಿಟಿಗಾಗಿ ನ್ಯಾವಿಗೇಶನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"ಆ್ಯಪ್ ಅಥವಾ ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಕ್ರಿಯೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 1f53ad4..d80e6b3 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Cuba lagi"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Siap!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Leret ke atas untuk mencapai laman utama"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Anda sudah sedia untuk mula menggunakan telefon anda"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Tetapan navigasi untuk kebolehaksesan"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Kongsi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak dibenarkan oleh apl atau organisasi anda"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 49daf19..ebf8485 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Opnieuw"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Dat gaat lekker."</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Klaar!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Swipe omhoog om naar het startscherm te gaan"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Je bent klaar om je telefoon te gebruiken"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Navigatie-instellingen voor toegankelijkheid"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Delen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Deze actie wordt niet toegestaan door de app of je organisatie"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 1b5a81d..e01dc08 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Spróbuj ponownie"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Super!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Samouczek <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Wszystko gotowe"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Aby przejść na stronę główną, przesuń w górę"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Teraz możesz zacząć używać telefonu"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Ustawienia nawigacji na potrzeby ułatwień dostępu"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Nie możesz wykonać tego działania, bo nie zezwala na to aplikacja lub Twoja organizacja"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index c254986..704afb6 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Tentar novamente"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Muito bem!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Deslize para cima para acessar a tela inicial"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Você já pode começar a usar seu smartphone"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Configurações de navegação para acessibilidade"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 810b909..56f0616 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Skúste to znova"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Výborne!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Návod <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Hotovo"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Potiahnutím nahor prejdete na plochu"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Telefón môžete začať používať"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Nastavenia navigácie pre dostupnosť"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Zdieľať"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index a171650..7f61e7d 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -74,14 +74,10 @@
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Zama futhi"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Kuhle!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"Okokufundisa <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
-    <!-- no translation found for allset_title (5021126669778966707) -->
-    <skip />
-    <!-- no translation found for allset_hint (459504134589971527) -->
-    <skip />
-    <!-- no translation found for allset_description (6350320429953234580) -->
-    <skip />
-    <!-- no translation found for allset_navigation_settings (417773244979225071) -->
-    <skip />
+    <string name="allset_title" msgid="5021126669778966707">"Konke kusethiwe!"</string>
+    <string name="allset_hint" msgid="459504134589971527">"Swayiphela phezulu ukuze uye ekhaya"</string>
+    <string name="allset_description" msgid="6350320429953234580">"Usulungele ukuqala ukusebenzisa ifoni yakho"</string>
+    <string name="allset_navigation_settings" msgid="417773244979225071"><annotation id="link">"Amasethingi wokufinyelela ukuzulazula"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Yabelana"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string>
diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml
index c7e864f..b4740e5 100644
--- a/quickstep/res/xml/indexable_launcher_prefs.xml
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -26,7 +26,7 @@
         android:key="pref_allowRotation"
         android:title="@string/allow_rotation_title"
         android:summary="@string/allow_rotation_desc"
-        android:defaultValue="@bool/allow_rotation"
+        android:defaultValue="false"
         android:persistent="true" />
 
 </PreferenceScreen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 14382d6..1b54211 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -491,7 +491,7 @@
             List<View> viewsToAnimate = new ArrayList<>();
 
             Workspace workspace = mLauncher.getWorkspace();
-            workspace.getVisiblePages().forEach(
+            workspace.forEachVisiblePage(
                     view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
 
             viewsToAnimate.add(mLauncher.getHotseat());
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 67ed5fb..5dcf84c 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
@@ -150,7 +151,7 @@
      * Shows appropriate hotseat education based on prediction enabled and migration states.
      */
     public void showEdu() {
-        mLauncher.getStateManager().goToState(NORMAL, true, () -> {
+        mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
             if (mPredictedItems.isEmpty()) {
                 // launcher has empty predictions set
                 Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
@@ -165,7 +166,7 @@
                         .collect(Collectors.toList()));
                 eduController.showEdu();
             }
-        });
+        }));
     }
 
     /**
@@ -255,8 +256,8 @@
             }
         }
         if (animate) {
-            animationSet.addListener(AnimationSuccessListener
-                    .forRunnable(this::removeOutlineDrawings));
+            animationSet.addListener(
+                    forSuccessCallback(this::removeOutlineDrawings));
             animationSet.start();
         } else {
             removeOutlineDrawings();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 5513c16..3af51d5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -16,12 +16,24 @@
 package com.android.launcher3.taskbar;
 
 import android.content.ContextWrapper;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
 import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -35,6 +47,7 @@
     private final DeviceProfile mDeviceProfile;
     private final LayoutInflater mLayoutInflater;
     private final TaskbarContainerView mTaskbarContainerView;
+    private final MyDragController mDragController;
 
     public TaskbarActivityContext(BaseQuickstepLauncher launcher) {
         super(launcher);
@@ -47,6 +60,7 @@
 
         mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater
                 .inflate(R.layout.taskbar, null, false);
+        mDragController = new MyDragController(this);
     }
 
     public TaskbarContainerView getTaskbarContainerView() {
@@ -72,4 +86,31 @@
     public Rect getFolderBoundingBox() {
         return mTaskbarContainerView.getFolderBoundingBox();
     }
+
+    @Override
+    public DragController getDragController() {
+        return mDragController;
+    }
+
+    private static class MyDragController extends DragController<TaskbarActivityContext> {
+        MyDragController(TaskbarActivityContext activity) {
+            super(activity);
+        }
+
+        @Override
+        protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
+                DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
+                ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+                float dragViewScaleOnDrop, DragOptions options) {
+            return null;
+        }
+
+        @Override
+        protected void exitDrag() { }
+
+        @Override
+        protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+            return null;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 0652d48..996d36a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -35,7 +35,7 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.states.StateAnimationConfig;
@@ -78,7 +78,7 @@
             mRecentsView.updateEmptyMessage();
         } else {
             builder.addListener(
-                    AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
+                    AnimatorListeners.forSuccessCallback(mRecentsView::resetTaskVisuals));
         }
 
         // Create or dismiss split screen select animations
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index b8caf81..19d1b32 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -38,7 +39,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
@@ -199,7 +200,7 @@
                         .animateWithVelocity(velocity);
             } else {
                 mLauncher.getStateManager().goToState(mEndState, true,
-                        () -> onSwipeInteractionCompleted(mEndState));
+                        forEndCallback(() -> onSwipeInteractionCompleted(mEndState)));
             }
             if (mStartState != mEndState) {
                 logHomeGesture();
@@ -214,7 +215,7 @@
             // Quickly return to the state we came from (we didn't move far).
             ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
             anim.setFloatValues(progress, 0);
-            anim.addListener(AnimationSuccessListener.forRunnable(
+            anim.addListener(AnimatorListeners.forSuccessCallback(
                     () -> onSwipeInteractionCompleted(mStartState)));
             anim.setDuration(80).start();
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 729e2cb..eb62110 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -172,13 +173,13 @@
         }
         mNormalToHintOverviewScrimAnimator = null;
         mCurrentAnimation.getTarget().addListener(newCancelListener(() ->
-                mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
+                mLauncher.getStateManager().goToState(OVERVIEW, true, forSuccessCallback(() -> {
                     mOverviewResistYAnim = AnimatorControllerWithResistance
                             .createRecentsResistanceFromOverviewAnim(mLauncher, null)
                             .createPlaybackController();
                     mReachedOverview = true;
                     maybeSwipeInteractionToOverviewComplete();
-                })));
+                }))));
 
         mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
         mCurrentAnimation.dispatchOnCancel();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 0f64abc..62687c5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
 import static com.android.launcher3.LauncherState.QUICK_SWITCH;
 import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -418,7 +419,7 @@
                         targetState.ordinal > mStartState.ordinal
                                 ? LAUNCHER_UNKNOWN_SWIPEUP
                                 : LAUNCHER_UNKNOWN_SWIPEDOWN));
-        mLauncher.getStateManager().goToState(targetState, false, this::clearState);
+        mLauncher.getStateManager().goToState(targetState, false, forEndCallback(this::clearState));
     }
 
     private void cancelAnimations() {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index fe10908..d04bfe9 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -63,7 +63,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.view.MotionEvent;
@@ -136,7 +135,6 @@
     protected final BaseActivityInterface<S, T> mActivityInterface;
     protected final InputConsumerProxy mInputConsumerProxy;
     protected final ActivityInitListener mActivityInitListener;
-    private final Handler mHandler = new Handler();
     // Callbacks to be made once the recents animation starts
     private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
     private final OnScrollChangedListener mOnRecentsScrollListener = this::onRecentsViewScroll;
@@ -148,9 +146,6 @@
     protected Runnable mGestureEndCallback;
     protected MultiStateCallback mStateCallback;
     protected boolean mCanceled;
-    // One time flag set when onConsumerAboutToBeSwitched() is called, indicating that certain
-    // shared animations should not be canceled when this handler is invalidated
-    private boolean mConsumerIsSwitching;
     private boolean mRecentsViewScrollLinked = false;
 
     private static int getFlagForIndex(int index, String name) {
@@ -1007,14 +1002,7 @@
         animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity);
     }
 
-    private int getLogGestureTaskIndex(@Nullable TaskView targetTask) {
-        return mRecentsView == null || targetTask == null
-                ? LOG_NO_OP_PAGE_INDEX
-                : mRecentsView.indexOfChild(targetTask);
-    }
-
-    private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask,
-            int pageIndex) {
+    private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
         StatsLogManager.EventEnum event;
         switch (endTarget) {
             case HOME:
@@ -1043,6 +1031,9 @@
             // We probably never received an animation controller, skip logging.
             return;
         }
+        int pageIndex = endTarget == LAST_TASK
+                ? LOG_NO_OP_PAGE_INDEX
+                : mRecentsView.getNextPage();
         // TODO: set correct container using the pageIndex
         logger.log(event);
     }
@@ -1211,6 +1202,7 @@
                 TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
                 startBounds,
                 destinationBounds,
+                mRecentsView.getPipCornerRadius(),
                 mRecentsView);
         // We would assume home and app window always in the same rotation While homeRotation
         // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
@@ -1293,18 +1285,18 @@
     }
 
     public void onConsumerAboutToBeSwitched() {
-        mConsumerIsSwitching = true;
         if (mActivity != null) {
             // In the off chance that the gesture ends before Launcher is started, we should clear
             // the callback here so that it doesn't update with the wrong state
             mActivity.clearRunOnceOnStartCallback();
+            resetLauncherListeners();
         }
         if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
             cancelCurrentAnimation();
         } else {
             mStateCallback.setStateOnUiThread(STATE_FINISH_WITH_NO_END);
+            reset();
         }
-        reset();
     }
 
     public boolean isCanceled() {
@@ -1315,14 +1307,13 @@
     private void resumeLastTask() {
         mRecentsAnimationController.finish(false /* toRecents */, null);
         ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
-        doLogGesture(LAST_TASK, null, getLogGestureTaskIndex(null));
+        doLogGesture(LAST_TASK, null);
         reset();
     }
 
     @UiThread
     private void startNewTask() {
         TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
-        int taskPageIndex = getLogGestureTaskIndex(taskToLaunch);
         startNewTask(success -> {
             if (!success) {
                 reset();
@@ -1331,7 +1322,7 @@
                 endLauncherTransitionController();
                 updateSysUiFlags(1 /* windowProgress == overview */);
             }
-            doLogGesture(NEW_TASK, taskToLaunch, taskPageIndex);
+            doLogGesture(NEW_TASK, taskToLaunch);
         });
     }
 
@@ -1365,39 +1356,29 @@
     }
 
     private void invalidateHandler() {
-        if (!mConsumerIsSwitching) {
-            if (!LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
-                    || mGestureState.getEndTarget() != RECENTS) {
-                mInputConsumerProxy.destroy();
-                mTaskAnimationManager.setLiveTileCleanUpHandler(null);
-            }
-            endRunningWindowAnim(false /* cancel */);
+        if (!LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
+                || mGestureState.getEndTarget() != RECENTS) {
+            mInputConsumerProxy.destroy();
+            mTaskAnimationManager.setLiveTileCleanUpHandler(null);
+        }
+        mInputConsumerProxy.unregisterCallback();
+        endRunningWindowAnim(false /* cancel */);
 
-            if (mGestureEndCallback != null) {
-                mGestureEndCallback.run();
-            }
+        if (mGestureEndCallback != null) {
+            mGestureEndCallback.run();
         }
 
-        mInputConsumerProxy.unregisterCallback();
         mActivityInitListener.unregister();
         ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
         mTaskSnapshot = null;
     }
 
     private void invalidateHandlerWithLauncher() {
-        if (!mConsumerIsSwitching) {
-            endLauncherTransitionController();
-            mRecentsView.onGestureAnimationEnd();
-        }
+        endLauncherTransitionController();
 
         mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
+        mRecentsView.onGestureAnimationEnd();
         resetLauncherListeners();
-
-        mHandler.post(() -> {
-            // Defer clearing the activity since invalidation can happen over multiple callbacks.
-            mActivity = null;
-            mRecentsView = null;
-        });
     }
 
     private void endLauncherTransitionController() {
@@ -1515,8 +1496,7 @@
                     () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
         ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
-        TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView();
-        doLogGesture(HOME, taskToLaunch, getLogGestureTaskIndex(taskToLaunch));
+        doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
     }
 
     /**
@@ -1547,8 +1527,7 @@
         }
 
         SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
-        TaskView taskToLaunch = mRecentsView.getCurrentPageTaskView();
-        doLogGesture(RECENTS, taskToLaunch, getLogGestureTaskIndex(taskToLaunch));
+        doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
         reset();
     }
 
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index dffee7f..c43d3c9 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.QUICK_SWITCH;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 
@@ -192,7 +193,8 @@
 
         closeOverlay();
         launcher.getStateManager().goToState(OVERVIEW,
-                launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
+                launcher.getStateManager().shouldAnimateStateChange(),
+                onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
         return true;
     }
 
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index f6018d1..39d8888 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -68,6 +68,12 @@
         MAIN_EXECUTOR.execute(() -> clearProxy());
     };
 
+    // Save the listeners passed into the proxy since when set/register these listeners,
+    // setProxy may not have been called, eg. OverviewProxyService is not connected yet.
+    private IPipAnimationListener mPendingPipAnimationListener;
+    private ISplitScreenListener mPendingSplitScreenListener;
+    private IStartingWindowListener mPendingStartingWindowListener;
+
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
     private boolean mLastShelfVisible;
@@ -116,6 +122,19 @@
         mShellTransitions = shellTransitions;
         mStartingWindow = startingWindow;
         linkToDeath();
+        // re-attach the listeners once missing due to setProxy has not been initialized yet.
+        if (mPendingPipAnimationListener != null && mPip != null) {
+            setPinnedStackAnimationListener(mPendingPipAnimationListener);
+            mPendingPipAnimationListener = null;
+        }
+        if (mPendingSplitScreenListener != null && mSplitScreen != null) {
+            registerSplitScreenListener(mPendingSplitScreenListener);
+            mPendingSplitScreenListener = null;
+        }
+        if (mPendingStartingWindowListener != null && mStartingWindow != null) {
+            setStartingWindowListener(mPendingStartingWindowListener);
+            mPendingStartingWindowListener = null;
+        }
     }
 
     public void clearProxy() {
@@ -390,6 +409,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
             }
+        } else {
+            mPendingPipAnimationListener = listener;
         }
     }
 
@@ -427,6 +448,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call registerSplitScreenListener");
             }
+        } else {
+            mPendingSplitScreenListener = listener;
         }
     }
 
@@ -438,6 +461,7 @@
                 Log.w(TAG, "Failed call unregisterSplitScreenListener");
             }
         }
+        mPendingSplitScreenListener = null;
     }
 
     public void setSideStageVisibility(boolean visible) {
@@ -590,6 +614,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setStartingWindowListener", e);
             }
+        } else {
+            mPendingStartingWindowListener = listener;
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 1aa64fa..66e0400 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -43,7 +43,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.quickstep.AnimatedFloat;
@@ -156,7 +156,7 @@
                         fadeAnim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
                     }
                     if (onEndRunnable != null) {
-                        fadeAnim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable));
+                        fadeAnim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
                     }
                     AnimatorSet animset = fadeAnim.buildAnim();
                     animset.setStartDelay(100);
@@ -174,7 +174,7 @@
                 anim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
             }
             if (onEndRunnable != null) {
-                anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable));
+                anim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
             }
         }
         AnimatorSet animset = anim.buildAnim();
@@ -205,10 +205,10 @@
         PendingAnimation fadeAnim = new PendingAnimation(300);
         fadeAnim.setViewAlpha(mFakeIconView, 0, ACCEL);
         if (onEndRunnable != null) {
-            fadeAnim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable));
+            fadeAnim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
         }
         AnimatorSet animset = fadeAnim.buildAnim();
-        rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable(animset::start));
+        rectAnim.addAnimatorListener(AnimatorListeners.forSuccessCallback(animset::start));
         mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim);
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 55972ad..1128a7c 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -38,7 +38,7 @@
 import androidx.appcompat.content.res.AppCompatResources;
 
 import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.views.ClipIconView;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -139,7 +139,7 @@
     }
 
     void fadeTaskViewAndRun(Runnable r) {
-        mFakeTaskView.animate().alpha(0).setListener(AnimationSuccessListener.forRunnable(r));
+        mFakeTaskView.animate().alpha(0).setListener(AnimatorListeners.forSuccessCallback(r));
     }
 
     @StringRes
@@ -348,8 +348,8 @@
                     mContext, getMockLauncherResId()));
             mFakeTaskView.setBackground(AppCompatResources.getDrawable(
                     mContext, getMockAppTaskThumbnailResId()));
-            mFakeTaskView.animate().alpha(1).setListener(AnimationSuccessListener.forRunnable(
-                    () -> mFakeTaskView.animate().cancel()));
+            mFakeTaskView.animate().alpha(1).setListener(
+                    AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel()));
             mFakePreviousTaskView.setBackground(AppCompatResources.getDrawable(
                     mContext, getMockPreviousAppTaskThumbnailResId()));
             mFakeIconView.setBackground(AppCompatResources.getDrawable(
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index d164c8c..e983f46 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -16,7 +16,6 @@
 
 package com.android.quickstep.util;
 
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
@@ -31,7 +30,6 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -46,7 +44,6 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -67,7 +64,8 @@
  * This class has initial default state assuming the device and foreground app have
  * no ({@link Surface#ROTATION_0} rotation.
  */
-public final class RecentsOrientedState implements SharedPreferences.OnSharedPreferenceChangeListener {
+public final class RecentsOrientedState implements
+        SharedPreferences.OnSharedPreferenceChangeListener {
 
     private static final String TAG = "RecentsOrientedState";
     private static final boolean DEBUG = false;
@@ -125,6 +123,7 @@
 
     private int mFlags;
     private int mPreviousRotation = ROTATION_0;
+    private boolean mListenersInitialized = false;
 
     // Combined int which encodes the full state.
     private int mStateId = 0;
@@ -152,19 +151,32 @@
         mFlags = sizeStrategy.rotationSupportedByActivity
                 ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
 
-        Resources res = context.getResources();
-        int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
-                * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
-        if (originalSmallestWidth < 600 && !mContext.getResources().getBoolean(
-                R.bool.allow_rotation)) {
-            mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
-        }
         mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
         mSettingsCache = SettingsCache.INSTANCE.get(mContext);
         initFlags();
     }
 
     /**
+     * Sets the device profile for the current state.
+     */
+    public void setDeviceProfile(DeviceProfile deviceProfile) {
+        boolean oldMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
+        setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.allowRotation);
+        if (mListenersInitialized) {
+            boolean newMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
+            // If isMultipleOrientationSupportedByDevice is changed, init or destroy listeners
+            // accordingly.
+            if (newMultipleOrientationsSupported != oldMultipleOrientationsSupported) {
+                if (newMultipleOrientationsSupported) {
+                    initMultipleOrientationListeners();
+                } else {
+                    destroyMultipleOrientationListeners();
+                }
+            }
+        }
+    }
+
+    /**
      * Sets the rotation for the recents activity, which could affect the appearance of task view.
      * @see #update(int, int)
      */
@@ -287,14 +299,24 @@
         updateHomeRotationSetting();
     }
 
+    private void initMultipleOrientationListeners() {
+        mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+        mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener);
+    }
+
+    private void destroyMultipleOrientationListeners() {
+        mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+        mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener);
+    }
+
     /**
      * Initializes any system values and registers corresponding change listeners. It must be
      * paired with {@link #destroyListeners()} call
      */
     public void initListeners() {
+        mListenersInitialized = true;
         if (isMultipleOrientationSupportedByDevice()) {
-            mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
-            mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener);
+            initMultipleOrientationListeners();
         }
         initFlags();
     }
@@ -303,9 +325,9 @@
      * Unregisters any previously registered listeners.
      */
     public void destroyListeners() {
+        mListenersInitialized = false;
         if (isMultipleOrientationSupportedByDevice()) {
-            mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
-            mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener);
+            destroyMultipleOrientationListeners();
         }
         setRotationWatcherEnabled(false);
     }
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index ab95138..0ea1fca 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -89,8 +89,7 @@
         int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
 
         // Add animation for all the visible workspace pages
-        workspace.getVisiblePages()
-                .forEach(page -> addAnimationForPage((CellLayout) page, totalRows));
+        workspace.forEachVisiblePage(page -> addAnimationForPage((CellLayout) page, totalRows));
 
         boolean workspaceClipChildren = workspace.getClipChildren();
         boolean workspaceClipToPadding = workspace.getClipToPadding();
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index a1240e0..b5570a7 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -89,6 +89,7 @@
      *                    different from the appBounds if user has swiped a certain distance and
      *                    Launcher has performed transform on the leash.
      * @param destinationBounds Bounds of the destination this animator ends to
+     * @param cornerRadius Corner radius in pixel value for PiP window
      */
     public SwipePipToHomeAnimator(int taskId,
             @NonNull ComponentName componentName,
@@ -97,6 +98,7 @@
             @NonNull Rect appBounds,
             @NonNull Rect startBounds,
             @NonNull Rect destinationBounds,
+            int cornerRadius,
             @NonNull View view) {
         mTaskId = taskId;
         mComponentName = componentName;
@@ -106,7 +108,7 @@
         mDestinationBounds.set(destinationBounds);
         mDestinationBoundsTransformed.set(mDestinationBounds);
         mDestinationBoundsAnimation.set(mDestinationBounds);
-        mSurfaceTransactionHelper = new PipSurfaceTransactionHelper();
+        mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(cornerRadius);
 
         if (sourceRectHint == null) {
             mSourceHintRectInsets = null;
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index f578ad1..b15bbf3 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -112,6 +112,7 @@
     public void setDp(DeviceProfile dp) {
         mDp = dp;
         mLayoutValid = false;
+        mOrientationState.setDeviceProfile(dp);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c268217..dfe0c43 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -111,6 +111,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.SpringProperty;
@@ -120,7 +121,6 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.DynamicResource;
@@ -494,6 +494,7 @@
 
     private final PinnedStackAnimationListener mIPipAnimationListener =
             new PinnedStackAnimationListener();
+    private int mPipCornerRadius;
 
     // Used to keep track of the last requested task list id, so that we do not request to load the
     // tasks again if we have already requested it and the task list has not changed
@@ -795,7 +796,7 @@
         mSyncTransactionApplier = new SurfaceTransactionApplier(this);
         mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
         RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
-        mIPipAnimationListener.setActivity(mActivity);
+        mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
                 mIPipAnimationListener);
         mOrientationState.initListeners();
@@ -815,7 +816,7 @@
         RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
         SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
-        mIPipAnimationListener.setActivity(null);
+        mIPipAnimationListener.setActivityAndRecentsView(null, null);
         mOrientationState.destroyListeners();
         mTaskOverlayFactory.removeListeners();
     }
@@ -1243,13 +1244,51 @@
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
-        resetPaddingFromTaskSize();
+
+        // Update DeviceProfile dependant state.
         DeviceProfile dp = mActivity.getDeviceProfile();
+        setOverviewGridEnabled(
+                mActivity.getStateManager().getState().displayOverviewTasksAsGrid(dp));
+
+        // Propagate DeviceProfile change event.
         mLiveTileTaskViewSimulator.setDp(dp);
         mActionsView.setDp(dp);
+        mOrientationState.setDeviceProfile(dp);
+
+        // Update RecentsView adn TaskView's DeviceProfile dependent layout.
+        updateOrientationHandler();
     }
 
-    private void resetPaddingFromTaskSize() {
+    private void updateOrientationHandler() {
+        // Handle orientation changes.
+        mOrientationHandler = mOrientationState.getOrientationHandler();
+        mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
+        setLayoutDirection(mIsRtl
+                ? View.LAYOUT_DIRECTION_RTL
+                : View.LAYOUT_DIRECTION_LTR);
+        mClearAllButton.setLayoutDirection(mIsRtl
+                ? View.LAYOUT_DIRECTION_LTR
+                : View.LAYOUT_DIRECTION_RTL);
+        mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
+        mActivity.getDragLayer().recreateControllers();
+        boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
+                || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
+        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
+                !mOrientationState.canRecentsActivityRotate() && isInLandscape);
+
+        // Update TaskView's DeviceProfile dependent layout.
+        updateChildTaskOrientations();
+
+        // Recalculate DeviceProfile dependent layout.
+        updateSizeAndPadding();
+
+        requestLayout();
+        // Reapply the current page to update page scrolls.
+        setCurrentPage(mCurrentPage);
+    }
+
+    // Update task size and padding that are dependent on DeviceProfile and insets.
+    private void updateSizeAndPadding() {
         DeviceProfile dp = mActivity.getDeviceProfile();
         getTaskSize(mTempRect);
         mTaskWidth = mTempRect.width();
@@ -1633,7 +1672,7 @@
             return;
         }
         AnimatorSet pa = setRecentsChangedOrientation(true);
-        pa.addListener(AnimationSuccessListener.forRunnable(() -> {
+        pa.addListener(AnimatorListeners.forSuccessCallback(() -> {
             setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
             mActivity.getDragLayer().recreateControllers();
             updateChildTaskOrientations();
@@ -2565,28 +2604,6 @@
         }
     }
 
-    private void updateOrientationHandler() {
-        mOrientationHandler = mOrientationState.getOrientationHandler();
-        mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
-        setLayoutDirection(mIsRtl
-                ? View.LAYOUT_DIRECTION_RTL
-                : View.LAYOUT_DIRECTION_LTR);
-        mClearAllButton.setLayoutDirection(mIsRtl
-                ? View.LAYOUT_DIRECTION_LTR
-                : View.LAYOUT_DIRECTION_RTL);
-        mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
-        mActivity.getDragLayer().recreateControllers();
-        boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
-                || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
-        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
-                !mOrientationState.canRecentsActivityRotate() && isInLandscape);
-        updateChildTaskOrientations();
-        resetPaddingFromTaskSize();
-        requestLayout();
-        // Reapply the current page to update page scrolls.
-        setCurrentPage(mCurrentPage);
-    }
-
     public RecentsOrientedState getPagedViewOrientedState() {
         return mOrientationState;
     }
@@ -3623,6 +3640,12 @@
      * capturing the snapshot at the same time.
      */
     public void switchToScreenshot(Runnable onFinishRunnable) {
+        if (mRecentsAnimationController == null) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
+            return;
+        }
         switchToScreenshot(mRunningTaskId == -1 ? null
                 : mRecentsAnimationController.screenshotTask(mRunningTaskId), onFinishRunnable);
     }
@@ -3672,7 +3695,6 @@
     public void onSecondaryWindowBoundsChanged() {
         // Invalidate the task view size
         setInsets(mInsets);
-        requestLayout();
     }
 
     /**
@@ -3765,6 +3787,14 @@
         mScrollListeners.remove(listener);
     }
 
+    /**
+     * @return Corner radius in pixel value for PiP window, which is updated via
+     *         {@link #mIPipAnimationListener}
+     */
+    public int getPipCornerRadius() {
+        return mPipCornerRadius;
+    }
+
     @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
@@ -3781,9 +3811,11 @@
     private static class PinnedStackAnimationListener<T extends BaseActivity> extends
             IPipAnimationListener.Stub {
         private T mActivity;
+        private RecentsView mRecentsView;
 
-        public void setActivity(T activity) {
+        public void setActivityAndRecentsView(T activity, RecentsView recentsView) {
             mActivity = activity;
+            mRecentsView = recentsView;
         }
 
         @Override
@@ -3796,6 +3828,13 @@
                 }
             });
         }
+
+        @Override
+        public void onPipCornerRadiusChanged(int cornerRadius) {
+            if (mRecentsView != null) {
+                mRecentsView.mPipCornerRadius = cornerRadius;
+            }
+        }
     }
 
     /** Get the color used for foreground scrimming the RecentsView for sharing. */
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index a791474..f8be5b6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -32,7 +32,6 @@
 import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
@@ -65,6 +64,7 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
@@ -146,6 +146,9 @@
 
     public static final long SCALE_ICON_DURATION = 120;
     private static final long DIM_ANIM_DURATION = 700;
+
+    private static final Interpolator FULLSCREEN_INTERPOLATOR = ACCEL_DEACCEL;
+
     /**
      * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
      * setting the touch bounds at construction, so we'd repeatedly be created many instances
@@ -961,7 +964,7 @@
 
     private void applyScale() {
         float scale = 1;
-        float fullScreenProgress = EXAGGERATED_EASE.getInterpolation(mFullscreenProgress);
+        float fullScreenProgress = FULLSCREEN_INTERPOLATOR.getInterpolation(mFullscreenProgress);
         scale *= Utilities.mapRange(fullScreenProgress, 1f, mFullscreenScale);
         setScaleX(scale);
         setScaleY(scale);
@@ -1404,7 +1407,7 @@
     }
 
     private float getFullscreenTrans(float endTranslation) {
-        float progress = ACCEL_DEACCEL.getInterpolation(mFullscreenProgress);
+        float progress = FULLSCREEN_INTERPOLATOR.getInterpolation(mFullscreenProgress);
         return Utilities.mapRange(progress, 0, endTranslation);
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index b6b7f50..9630d27 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -62,6 +62,7 @@
 import com.android.quickstep.views.RecentsView;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
@@ -142,10 +143,19 @@
         }
     }
 
+    @Before
+    public void setUp() {
+        mLauncher.onTestStart();
+    }
+
     @After
-    public void verifyLauncherState() {
-        // Limits UI tests affecting tests running after them.
-        AbstractQuickStepTest.checkDetectedLeaks(mLauncher);
+    public void tearDown() {
+        try {
+            // Limits UI tests affecting tests running after them.
+            AbstractQuickStepTest.checkDetectedLeaks(mLauncher);
+        } finally {
+            mLauncher.onTestFinish();
+        }
     }
 
     // b/143488140
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 77f0118..b3b0d2b 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Muurpapier en styl"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Tuis-instellings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Laat toe dat tuisskerm gedraai word"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 4030f1f..502d5eb 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Baggrund og stil"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærm"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index d506136..56f31bf 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o más elementos"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Fondo de pantalla y estilo"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configuración de pantalla principal"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir la rotación de la pantalla principal"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 066d54f..f1ae4d5 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد یا بیشتر"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواری‌ها"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"کاغذدیواری و سبک"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"قابل‌چرخش بودن صفحه اصلی"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index b4f4706..27f045e 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಐಟಂಗಳು"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಅಥವಾ ಹೆಚ್ಚಿನ ಐಟಂಗಳು"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ವಾಲ್‌ಪೇಪರ್ ಮತ್ತು ಶೈಲಿ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 5893ceb..9465be5 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atau lebih banyak item"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Kertas dinding &amp; gaya"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Tetapan laman utama"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index bfbd011..58d1d98 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Achtergrond en stijl"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Instellingen start"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgezet door je beheerder"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Draaien van startscherm toestaan"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index bfa7973..acce00a 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -103,8 +103,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementy"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, liczba elementów: <xliff:g id="SIZE">%2$d</xliff:g> lub więcej"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta i styl"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia ekranu głównego"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Zezwalaj na obrót ekranu głównego"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f7f7aa1..fd793d4 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Plano de fundo e estilo"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configurações da tela inicial"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação da tela inicial"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 6c4810e..e23e7a9 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -103,8 +103,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> položky"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> alebo viac položiek"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Tapeta a štýl"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia plochy"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Povoliť otáčanie plochy"</string>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
deleted file mode 100644
index 09bdaaf..0000000
--- a/res/values-sw600dp/config.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
-    <bool name="allow_rotation">true</bool>
-</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index ead666c..47a88f2 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -3,7 +3,7 @@
 
      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
+     You may obtain a copy of the License at`
 
           http://www.apache.org/licenses/LICENSE-2.0
 
@@ -15,13 +15,6 @@
 -->
 
 <resources>
-<!-- All Apps -->
-    <dimen name="all_apps_background_canvas_width">850dp</dimen>
-    <dimen name="all_apps_background_canvas_height">525dp</dimen>
-
-<!-- Widget tray -->
-    <dimen name="widget_section_indent">56dp</dimen>
-
 <!-- DragController -->
     <dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
 </resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
deleted file mode 100644
index 33fc553..0000000
--- a/res/values-sw720dp/config.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<resources>
-
-<!-- All Apps & Widgets -->
-    <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
-    <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
-</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
deleted file mode 100644
index 691219a..0000000
--- a/res/values-sw720dp/dimens.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <!-- All Apps -->
-    <dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
-    <dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
-
-    <!-- Fast scroll -->
-    <dimen name="fastscroll_popup_width">75dp</dimen>
-    <dimen name="fastscroll_popup_height">62dp</dimen>
-    <dimen name="fastscroll_popup_padding">13dp</dimen>
-    <dimen name="fastscroll_popup_text_size">32dp</dimen>
-
-</resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
deleted file mode 100644
index c1e6eca..0000000
--- a/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-    <!-- Workspace -->
-    <style name="DropTargetButton" parent="DropTargetButtonBase">
-        <item name="android:paddingLeft">60dp</item>
-        <item name="android:paddingRight">60dp</item>
-        <item name="android:shadowDx">0.0</item>
-        <item name="android:shadowDy">0.0</item>
-        <item name="android:shadowRadius">2.0</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index d29e15d..dbdcf17 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -97,8 +97,7 @@
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> izinto"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> noma izinto eziningi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
-    <!-- no translation found for styles_wallpaper_button_text (8216961355289236794) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Isithombe sangemuva nesitayela"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Izilungiselelo zasekhaya"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Vumela ukuphendukiswa kwesikrini sasekhaya"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 2e5ecba..e65c652 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,7 +1,6 @@
 <resources>
     <!-- Miscellaneous -->
     <bool name="config_largeHeap">false</bool>
-    <bool name="allow_rotation">false</bool>
 
     <integer name="extracted_color_gradient_alpha">153</integer>
 
@@ -137,7 +136,7 @@
     <item name="swipe_up_trans_y_stiffness" type="dimen" format="float">200</item>
 
     <item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
-    <item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">100</item>
+    <item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
 
 
     <item name="swipe_up_rect_2_x_damping_ratio" type="dimen" format="float">1</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0adafaf..3267a5d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -138,7 +138,6 @@
     <dimen name="widget_section_icon_size">40dp</dimen>
     <dimen name="widget_section_vertical_padding">8dp</dimen>
     <dimen name="widget_section_horizontal_padding">16dp</dimen>
-    <dimen name="widget_section_indent">0dp</dimen>
 
     <dimen name="widget_row_padding">8dp</dimen>
     <dimen name="widget_row_divider">2dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b270b94..dd72114 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -42,7 +42,7 @@
         <item name="isWorkspaceDarkText">false</item>
         <item name="workspaceTextColor">@color/workspace_text_color_light</item>
         <item name="workspaceShadowColor">#B0000000</item>
-        <item name="workspaceAmbientShadowColor">#00000000</item>
+        <item name="workspaceAmbientShadowColor">#40000000</item>
         <item name="workspaceKeyShadowColor">#89000000</item>
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
@@ -241,7 +241,7 @@
         <item name="android:shadowRadius">2.0</item>
         <item name="android:shadowColor">?attr/workspaceShadowColor</item>
         <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
-        <item name="ambientShadowBlur">2.5dp</item>
+        <item name="ambientShadowBlur">1.5dp</item>
         <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
         <item name="keyShadowBlur">.5dp</item>
         <item name="keyShadowOffsetX">.5dp</item>
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 8195cc1..90de498 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -45,7 +45,7 @@
         android:key="pref_allowRotation"
         android:title="@string/allow_rotation_title"
         android:summary="@string/allow_rotation_desc"
-        android:defaultValue="@bool/allow_rotation"
+        android:defaultValue="false"
         android:persistent="true"
         launcher:logIdOn="615"
         launcher:logIdOff="616" />
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index f64ce5c..9778b61 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -132,6 +132,7 @@
 
     private final ViewCache mViewCache = new ViewCache();
 
+    @Override
     public ViewCache getViewCache() {
         return mViewCache;
     }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 00317f7..af77bf8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -74,7 +74,6 @@
 
     protected final Launcher mLauncher;
 
-    private int mBottomDragPadding;
     protected DropTargetBar mDropTargetBar;
 
     /** Whether this drop target is active for the current drag */
@@ -103,7 +102,6 @@
         mLauncher = Launcher.getLauncher(context);
 
         Resources resources = getResources();
-        mBottomDragPadding = resources.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
         mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
     }
 
@@ -276,7 +274,7 @@
     @Override
     public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
         super.getHitRect(outRect);
-        outRect.bottom += mBottomDragPadding;
+        outRect.bottom += mLauncher.getDeviceProfile().dropTargetDragPaddingPx;
 
         sTempCords[0] = sTempCords[1] = 0;
         mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index d5860dc..e5ad243 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,10 +16,12 @@
 
 package com.android.launcher3;
 
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static com.android.launcher3.ResourceUtils.pxFromDp;
 import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -61,6 +63,7 @@
     public final boolean isPhone;
     public final boolean transposeLayoutWithOrientation;
     public final boolean isTwoPanels;
+    public final boolean allowRotation;
 
     // Device properties in current orientation
     public final boolean isLandscape;
@@ -172,6 +175,8 @@
 
     // Drop Target
     public int dropTargetBarSizePx;
+    public int dropTargetDragPaddingPx;
+    public int dropTargetTextSizePx;
 
     // Insets
     private final Rect mInsets = new Rect();
@@ -190,6 +195,9 @@
     // How much of the bottom inset is due to Taskbar rather than other system elements.
     public int nonOverlappingTaskbarInset;
 
+    // DragController
+    public int flingToDeleteThresholdVelocity;
+
     DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
             boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
             boolean useTwoPanels) {
@@ -210,7 +218,12 @@
         int nonFinalAvailableHeightPx = windowBounds.availableSize.y;
 
         mInfo = info;
-        isTablet = info.isTablet(windowBounds);
+        // If the device's pixel density was scaled (usually via settings for A11y), use the
+        // original dimensions to determine if rotation is allowed of not.
+        float originalSmallestWidth = dpiFromPx(Math.min(widthPx, heightPx), DENSITY_DEVICE_STABLE);
+        allowRotation = originalSmallestWidth >= MIN_TABLET_WIDTH;
+        // Tablet UI does not support emulated landscape.
+        isTablet = allowRotation && info.isTablet(windowBounds);
         isPhone = !isTablet;
         isTwoPanels = isTablet && useTwoPanels;
 
@@ -287,7 +300,11 @@
 
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
+
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
+        dropTargetDragPaddingPx = res.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
+        dropTargetTextSizePx = res.getDimensionPixelSize(R.dimen.drop_target_text_size);
+
         workspaceSpringLoadedBottomSpace =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
 
@@ -354,6 +371,9 @@
         }
         updateWorkspacePadding();
 
+        flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
+                R.dimen.drag_flingToDeleteMinVelocity);
+
         // This is done last, after iconSizePx is calculated above.
         Path dotPath = GraphicsUtils.getShapePath(DEFAULT_DOT_SIZE);
         mDotRendererWorkSpace = new DotRenderer(iconSizePx, dotPath, DEFAULT_DOT_SIZE);
@@ -821,6 +841,7 @@
         writer.println(prefix + "DeviceProfile:");
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
 
+        writer.println(prefix + "\tallowRotation:" + allowRotation);
         writer.println(prefix + "\tisTablet:" + isTablet);
         writer.println(prefix + "\tisPhone:" + isPhone);
         writer.println(prefix + "\ttransposeLayoutWithOrientation:"
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index c768493..4a1b084 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewDebug;
@@ -114,6 +115,7 @@
         }
         setLayoutParams(lp);
         for (ButtonDropTarget button : mDropTargets) {
+            button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx);
             button.setToolTipLocation(tooltipLocation);
         }
     }
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 4312939..20c6938 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3;
 
+import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
+
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -25,7 +27,7 @@
 import android.widget.EditText;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.views.ActivityContext;
 
 
 /**
@@ -99,7 +101,7 @@
     }
 
     public void hideKeyboard() {
-        UiThreadHelper.hideKeyboardAsync(Launcher.getLauncher(getContext()), getWindowToken());
+        hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
     }
 
     private boolean showSoftInput() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 491d95d..7b67807 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -124,6 +124,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.LauncherDragController;
 import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.BitmapRenderer;
@@ -428,7 +429,7 @@
         mIconCache = app.getIconCache();
         mAccessibilityDelegate = createAccessibilityDelegate();
 
-        mDragController = new DragController(this);
+        mDragController = new LauncherDragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new StateManager<>(this, NORMAL);
 
@@ -904,7 +905,7 @@
         } else {
             mOverlayManager.onActivityStopped(this);
         }
-
+        hideKeyboard();
         logStopAndResume(false /* isResume */);
         mAppWidgetHost.setActivityStarted(false);
         NotificationListener.removeNotificationsChangedListener();
@@ -1458,7 +1459,7 @@
                 && AbstractFloatingView.getTopOpenView(this) == null;
         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
         boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
-
+        hideKeyboard();
         if (isActionMain) {
             if (!internalStateHandled) {
                 // In all these cases, only animate if we're already on home
@@ -1480,9 +1481,6 @@
                 }
             }
 
-            // Handle HOME_INTENT
-            hideKeyboard();
-
             if (mLauncherCallbacks != null) {
                 mLauncherCallbacks.onHomeIntent(internalStateHandled);
             }
@@ -2154,12 +2152,29 @@
      */
     @Override
     public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
+        bindItems(items, forceAnimateIcons, /* focusFirstItemForAccessibility= */ false);
+    }
+
+
+    /**
+     * Bind the items start-end from the list.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     *
+     * @param focusFirstItemForAccessibility true iff the first item to be added to the workspace
+     *                                       should be focused for accessibility.
+     */
+    public void bindItems(
+            final List<ItemInfo> items,
+            final boolean forceAnimateIcons,
+            final boolean focusFirstItemForAccessibility) {
         // Get the list of added items and intersect them with the set of items here
         final Collection<Animator> bounceAnims = new ArrayList<>();
         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
         Workspace workspace = mWorkspace;
         int newItemsScreenId = -1;
         int end = items.size();
+        View newView = null;
         for (int i = 0; i < end; i++) {
             final ItemInfo item = items.get(i);
 
@@ -2224,12 +2239,25 @@
                 bounceAnims.add(createNewAppBounceAnimation(view, i));
                 newItemsScreenId = item.screenId;
             }
+
+            if (newView == null) {
+                newView = view;
+            }
         }
 
-        // Animate to the correct page
+        View viewToFocus = newView;
+        // Animate to the correct pager
         if (animateIcons && newItemsScreenId > -1) {
             AnimatorSet anim = new AnimatorSet();
             anim.playTogether(bounceAnims);
+            if (focusFirstItemForAccessibility && viewToFocus != null) {
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                    }
+                });
+            }
 
             int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
             final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
@@ -2252,6 +2280,8 @@
             } else {
                 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
             }
+        } else if (focusFirstItemForAccessibility && viewToFocus != null) {
+            viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
         }
         workspace.requestLayout();
     }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index fb21698..b26a7ea 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -59,7 +59,7 @@
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
-import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * An abstraction of the original Workspace which supports browsing through a
@@ -295,18 +295,16 @@
     }
 
     /**
-     * Returns the currently visible pages.
+     * Executes the callback against each visible page
      */
-    public Iterable<View> getVisiblePages() {
+    public void forEachVisiblePage(Consumer<View> callback) {
         int panelCount = getPanelCount();
-        List<View> visiblePages = new ArrayList<>(panelCount);
         for (int i = mCurrentPage; i < mCurrentPage + panelCount; i++) {
             View page = getPageAt(i);
             if (page != null) {
-                visiblePages.add(page);
+                callback.accept(page);
             }
         }
-        return visiblePages;
     }
 
     /**
@@ -461,11 +459,6 @@
         }
     }
 
-    // we moved this functionality to a helper function so SmoothPagedView can reuse it
-    protected boolean computeScrollHelper() {
-        return computeScrollHelper(true);
-    }
-
     protected void announcePageForAccessibility() {
         if (isAccessibilityEnabled(getContext())) {
             // Notify the user when the page changes
@@ -473,7 +466,7 @@
         }
     }
 
-    protected boolean computeScrollHelper(boolean shouldInvalidate) {
+    protected boolean computeScrollHelper() {
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
             int oldPos = mOrientationHandler.getPrimaryScroll(this);
@@ -481,23 +474,29 @@
             if (oldPos != newPos) {
                 mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrX());
             }
-            if (shouldInvalidate) {
-                if (mAllowOverScroll) {
-                    if (newPos < mMinScroll && oldPos >= mMinScroll) {
-                        mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
-                        mScroller.abortAnimation();
-                    } else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
-                        mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
-                        mScroller.abortAnimation();
-                    }
+
+            if (mAllowOverScroll) {
+                if (newPos < mMinScroll && oldPos >= mMinScroll) {
+                    mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
+                    mScroller.abortAnimation();
+                } else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
+                    mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
+                    mScroller.abortAnimation();
                 }
-
-                invalidate();
             }
-            return true;
-        } else if (mNextPage != INVALID_PAGE && shouldInvalidate) {
-            sendScrollAccessibilityEvent();
 
+            // If the scroller has scrolled to the final position and there is no edge effect, then
+            // finish the scroller to skip waiting for additional settling
+            int finalPos = mOrientationHandler.getPrimaryValue(mScroller.getFinalX(),
+                    mScroller.getFinalY());
+            if (newPos == finalPos && mEdgeGlowLeft.isFinished() && mEdgeGlowRight.isFinished()) {
+                mScroller.abortAnimation();
+            }
+
+            invalidate();
+            return true;
+        } else if (mNextPage != INVALID_PAGE) {
+            sendScrollAccessibilityEvent();
             int prevPage = mCurrentPage;
             mCurrentPage = validateNewPage(mNextPage);
             mNextPage = INVALID_PAGE;
@@ -835,18 +834,25 @@
             return;
         }
 
+        // Add the current page's views as focusable and the next possible page's too. If the
+        // last focus change action was left then the left neighbour's views will be added, and
+        // if it was right then the right neighbour's views will be added.
+        // Unfortunately mCurrentPage can be outdated if there were multiple control actions in a
+        // short period of time, but mNextPage is up to date because it is always updated by
+        // method snapToPage.
+        int nextPage = getNextPage();
         // XXX-RTL: This will be fixed in a future CL
-        if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
-            getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
+        if (nextPage >= 0 && nextPage < getPageCount()) {
+            getPageAt(nextPage).addFocusables(views, direction, focusableMode);
         }
         if (direction == View.FOCUS_LEFT) {
-            if (mCurrentPage > 0) {
-                int nextPage = validateNewPage(mCurrentPage - 1);
+            if (nextPage > 0) {
+                nextPage = validateNewPage(nextPage - 1);
                 getPageAt(nextPage).addFocusables(views, direction, focusableMode);
             }
         } else if (direction == View.FOCUS_RIGHT) {
-            if (mCurrentPage < getPageCount() - 1) {
-                int nextPage = validateNewPage(mCurrentPage + 1);
+            if (nextPage < getPageCount() - 1) {
+                nextPage = validateNewPage(nextPage + 1);
                 getPageAt(nextPage).addFocusables(views, direction, focusableMode);
             }
         }
@@ -1034,7 +1040,7 @@
         // Try canceling the long press. It could also have been scheduled
         // by a distant descendant, so use the mAllowLongPress flag to block
         // everything
-        getVisiblePages().forEach(View::cancelLongPress);
+        forEachVisiblePage(View::cancelLongPress);
     }
 
     protected float getScrollProgress(int screenCenter, View v, int page) {
@@ -1416,6 +1422,14 @@
     @Override
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
+
+        // In case the device is controlled by a controller, mCurrentPage isn't updated properly
+        // which results in incorrect navigation
+        int nextPage = getNextPage();
+        if (nextPage != mCurrentPage) {
+            setCurrentPage(nextPage);
+        }
+
         int page = indexToPage(indexOfChild(child));
         if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
             snapToPage(page);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7c5f99e..5ba7623 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -1163,10 +1164,6 @@
         mWallpaperOffset.syncWithScroll();
     }
 
-    public void computeScrollWithoutInvalidation() {
-        computeScrollHelper(false);
-    }
-
     @Override
     public void announceForAccessibility(CharSequence text) {
         // Don't announce if apps is on top of us.
@@ -1950,8 +1947,8 @@
             }
             parent.onDropChild(cell);
 
-            mLauncher.getStateManager().goToState(
-                    NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
+            mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,
+                    onCompleteRunnable == null ? null : forSuccessCallback(onCompleteRunnable));
             mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
                     .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
@@ -2974,7 +2971,7 @@
 
         List<CellLayout> cellLayouts = new ArrayList<>(getPanelCount() + 1);
         cellLayouts.add(getHotseat());
-        getVisiblePages().forEach(page -> cellLayouts.add((CellLayout) page));
+        forEachVisiblePage(page -> cellLayouts.add((CellLayout) page));
 
         // Order: App icons, app in folder. Items in hotseat get returned first.
         if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index dbdfb2b..2b36f19 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -3,6 +3,7 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
 
 import android.appwidget.AppWidgetProviderInfo;
@@ -220,27 +221,26 @@
         } else if (action == ADD_TO_WORKSPACE) {
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
-            mLauncher.getStateManager().goToState(NORMAL, true, new Runnable() {
+            mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
+                if (item instanceof AppInfo) {
+                    WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
+                    mLauncher.getModelWriter().addItemToDatabase(info,
+                            Favorites.CONTAINER_DESKTOP,
+                            screenId, coordinates[0], coordinates[1]);
 
-                @Override
-                public void run() {
-                    if (item instanceof AppInfo) {
-                        WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
-                        mLauncher.getModelWriter().addItemToDatabase(info,
-                                Favorites.CONTAINER_DESKTOP,
-                                screenId, coordinates[0], coordinates[1]);
-
-                        mLauncher.bindItems(Collections.singletonList(info), true);
-                        announceConfirmation(R.string.item_added_to_workspace);
-                    } else if (item instanceof PendingAddItemInfo) {
-                        PendingAddItemInfo info = (PendingAddItemInfo) item;
-                        Workspace workspace = mLauncher.getWorkspace();
-                        workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
-                        mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
-                                screenId, coordinates, info.spanX, info.spanY);
-                    }
+                    mLauncher.bindItems(
+                            Collections.singletonList(info),
+                            /* forceAnimateIcons= */ true,
+                            /* focusFirstItemForAccessibility= */ true);
+                    announceConfirmation(R.string.item_added_to_workspace);
+                } else if (item instanceof PendingAddItemInfo) {
+                    PendingAddItemInfo info = (PendingAddItemInfo) item;
+                    Workspace workspace = mLauncher.getWorkspace();
+                    workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
+                    mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
+                            screenId, coordinates, info.spanX, info.spanY);
                 }
-            });
+            }));
             return true;
         } else if (action == MOVE_TO_WORKSPACE) {
             Folder folder = Folder.getOpen(mLauncher);
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index 1733e5d..f96afa8 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.accessibility;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 
 import android.view.KeyEvent;
 import android.view.View;
@@ -67,19 +68,14 @@
             final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
-            Runnable onComplete = new Runnable() {
-                @Override
-                public void run() {
-                    mLauncher.getModelWriter().addItemToDatabase(info,
-                            LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                            screenId, coordinates[0], coordinates[1]);
-                    mLauncher.bindItems(Collections.singletonList(info), true);
-                    AbstractFloatingView.closeAllOpenViews(mLauncher);
-                    announceConfirmation(R.string.item_added_to_workspace);
-                }
-            };
-
-            mLauncher.getStateManager().goToState(NORMAL, true, onComplete);
+            mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
+                mLauncher.getModelWriter().addItemToDatabase(info,
+                        LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                        screenId, coordinates[0], coordinates[1]);
+                mLauncher.bindItems(Collections.singletonList(info), true);
+                AbstractFloatingView.closeAllOpenViews(mLauncher);
+                announceConfirmation(R.string.item_added_to_workspace);
+            }));
             return true;
         } else if (action == DISMISS_NOTIFICATION) {
             if (!(host instanceof NotificationMainView)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 90f2d7c..2cfbdf9 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -28,7 +28,7 @@
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
 import android.util.FloatProperty;
 import android.view.View;
@@ -39,7 +39,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.config.FeatureFlags;
@@ -189,8 +189,8 @@
         mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
     }
 
-    public AnimatorListenerAdapter getProgressAnimatorListener() {
-        return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd);
+    public AnimatorListener getProgressAnimatorListener() {
+        return AnimatorListeners.forSuccessCallback(this::onProgressAnimationEnd);
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 6957850..88d95fa 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -30,6 +30,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.TreeMap;
 
 /**
@@ -181,11 +182,10 @@
      * Sets results list for search
      */
     public boolean setSearchResults(ArrayList<AdapterItem> results) {
-        if (results == null || mSearchResults != results) {
-            boolean same = mSearchResults != null && mSearchResults.equals(results);
+        if (!Objects.equals(results, mSearchResults)) {
             mSearchResults = results;
             updateAdapterItems();
-            return !same;
+            return true;
         }
         return false;
     }
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index 9905e81..a312070 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -40,24 +40,4 @@
 
     public abstract void onAnimationSuccess(Animator animator);
 
-    /**
-     * Returns an AnimationSuccessListener which runs the provided action on success
-     */
-    public static AnimationSuccessListener forRunnable(Runnable r) {
-        return new RunnableSuccessListener(r);
-    }
-
-    private static class RunnableSuccessListener extends AnimationSuccessListener {
-
-        private final Runnable mRunnable;
-
-        private RunnableSuccessListener(Runnable r) {
-            mRunnable = r;
-        }
-
-        @Override
-        public void onAnimationSuccess(Animator animator) {
-            mRunnable.run();
-        }
-    }
 }
diff --git a/src/com/android/launcher3/anim/AnimatorListeners.java b/src/com/android/launcher3/anim/AnimatorListeners.java
new file mode 100644
index 0000000..3799700
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimatorListeners.java
@@ -0,0 +1,98 @@
+/*
+ * 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.anim;
+
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import java.util.function.Consumer;
+
+/**
+ * Utility class for creating common {@link AnimatorListener}
+ */
+public class AnimatorListeners {
+
+    /**
+     * Returns an AnimatorListener which executes the callback on successful animation completion
+     */
+    public static AnimatorListener forSuccessCallback(Runnable callback) {
+        return new RunnableSuccessListener(callback);
+    }
+
+    /**
+     * Returns an AnimatorListener which executes the callback on animation completion,
+     * with the boolean representing success
+     */
+    public static AnimatorListener forEndCallback(Consumer<Boolean> callback) {
+        return new EndStateCallbackWrapper(callback);
+    }
+
+    /**
+     * Returns an AnimatorListener which executes the callback on animation completion
+     */
+    public static AnimatorListener forEndCallback(Runnable callback) {
+        return new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation, boolean isReverse) {
+                callback.run();
+            }
+        };
+    }
+
+    private static class EndStateCallbackWrapper extends AnimatorListenerAdapter {
+
+        private final Consumer<Boolean> mListener;
+        private boolean mListenerCalled = false;
+
+        EndStateCallbackWrapper(Consumer<Boolean> listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (!mListenerCalled) {
+                mListenerCalled = true;
+                mListener.accept(false);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (!mListenerCalled) {
+                ValueAnimator anim = (ValueAnimator) animation;
+                mListener.accept(anim.getAnimatedFraction() > SUCCESS_TRANSITION_PROGRESS);
+            }
+        }
+    }
+
+    private static class RunnableSuccessListener extends AnimationSuccessListener {
+
+        private final Runnable mRunnable;
+
+        private RunnableSuccessListener(Runnable r) {
+            mRunnable = r;
+        }
+
+        @Override
+        public void onAnimationSuccess(Animator animator) {
+            mRunnable.run();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 8057475..01f7de6 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -15,13 +15,11 @@
  */
 package com.android.launcher3.anim;
 
-import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
 import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -182,32 +180,6 @@
         if (mProgressAnimator == null) {
             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
         }
-        mProgressAnimator.addListener(new EndStateCallbackWrapper(listener));
-    }
-
-    private static class EndStateCallbackWrapper extends AnimatorListenerAdapter {
-
-        private final Consumer<Boolean> mListener;
-        private boolean mCalled = false;
-
-        EndStateCallbackWrapper(Consumer<Boolean> listener) {
-            mListener = listener;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            if (!mCalled) {
-                mCalled = true;
-                mListener.accept(false);
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (!mCalled) {
-                ValueAnimator anim = (ValueAnimator) animation;
-                mListener.accept(anim.getAnimatedFraction() > SUCCESS_TRANSITION_PROGRESS);
-            }
-        }
+        mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
     }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 6b42d98..7ef43ed 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -174,12 +174,12 @@
             "Replace Smartspace with the enhanced version. "
                     + "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_FEEDBACK = new DeviceFlag(
+    public static final BooleanFlag ENABLE_SMARTSPACE_FEEDBACK = getDebugFlag(
             "ENABLE_SMARTSPACE_FEEDBACK", true,
             "Adds a menu option to send feedback for Enhanced Smartspace.");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = new DeviceFlag(
-            "ENABLE_SMARTSPACE_DISMISS", false,
+    public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag(
+            "ENABLE_SMARTSPACE_DISMISS", true,
             "Adds a menu option to dismiss the current Enhanced Smartspace card.");
 
     public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 575b8fd..5731db4 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,45 +16,37 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
-import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.Utilities.ATLEAST_Q;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.ComponentName;
-import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.view.DragEvent;
-import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 import java.util.Optional;
 
 /**
  * Class for initiating a drag within a view or across multiple views.
+ * @param <T>
  */
-public class DragController implements DragDriver.EventListener, TouchController {
-    private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+public abstract class DragController<T extends ActivityContext>
+        implements DragDriver.EventListener, TouchController {
 
     /**
      * When a drag is started from a deep press, you need to drag this much farther than normal to
@@ -62,8 +54,7 @@
      */
     private static final int DEEP_PRESS_DISTANCE_FACTOR = 3;
 
-    private final Launcher mLauncher;
-    private final FlingToDeleteHelper mFlingToDeleteHelper;
+    protected final T mActivity;
 
     // temporaries to avoid gc thrash
     private final Rect mRectTemp = new Rect();
@@ -73,30 +64,30 @@
      * Drag driver for the current drag/drop operation, or null if there is no active DND operation.
      * It's null during accessible drag operations.
      */
-    private DragDriver mDragDriver = null;
+    protected DragDriver mDragDriver = null;
 
     /** Options controlling the drag behavior. */
-    private DragOptions mOptions;
+    protected DragOptions mOptions;
 
     /** Coordinate for motion down event */
-    private final Point mMotionDown = new Point();
+    protected final Point mMotionDown = new Point();
     /** Coordinate for last touch event **/
-    private final Point mLastTouch = new Point();
+    protected final Point mLastTouch = new Point();
 
     private final Point mTmpPoint = new Point();
 
-    private DropTarget.DragObject mDragObject;
+    protected DropTarget.DragObject mDragObject;
 
     /** Who can receive drop events */
     private final ArrayList<DropTarget> mDropTargets = new ArrayList<>();
     private final ArrayList<DragListener> mListeners = new ArrayList<>();
 
-    private DropTarget mLastDropTarget;
+    protected DropTarget mLastDropTarget;
 
     private int mLastTouchClassification;
-    private int mDistanceSinceScroll = 0;
+    protected int mDistanceSinceScroll = 0;
 
-    private boolean mIsInPreDrag;
+    protected boolean mIsInPreDrag;
 
     /**
      * Interface to receive notifications when a drag starts or stops
@@ -119,9 +110,8 @@
     /**
      * Used to create a new DragLayer from XML.
      */
-    public DragController(Launcher launcher) {
-        mLauncher = launcher;
-        mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
+    public DragController(T activity) {
+        mActivity = activity;
     }
 
     /**
@@ -198,7 +188,7 @@
                 options);
     }
 
-    private DragView startDrag(
+    protected abstract DragView startDrag(
             @Nullable Drawable drawable,
             @Nullable View view,
             DraggableView originalView,
@@ -210,98 +200,9 @@
             Rect dragRegion,
             float initialDragViewScale,
             float dragViewScaleOnDrop,
-            DragOptions options) {
-        if (PROFILE_DRAWING_DURING_DRAG) {
-            android.os.Debug.startMethodTracing("Launcher");
-        }
+            DragOptions options);
 
-        mLauncher.hideKeyboard();
-        AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
-
-        mOptions = options;
-        if (mOptions.simulatedDndStartPoint != null) {
-            mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
-            mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
-        }
-
-        final int registrationX = mMotionDown.x - dragLayerX;
-        final int registrationY = mMotionDown.y - dragLayerY;
-
-        final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
-        final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
-
-        mLastDropTarget = null;
-
-        mDragObject = new DropTarget.DragObject(mLauncher.getApplicationContext());
-        mDragObject.originalView = originalView;
-
-        mIsInPreDrag = mOptions.preDragCondition != null
-                && !mOptions.preDragCondition.shouldStartDrag(0);
-
-        final Resources res = mLauncher.getResources();
-        final float scaleDps = mIsInPreDrag
-                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
-        final DragView dragView = mDragObject.dragView = drawable != null
-                ? new DragView(
-                    mLauncher,
-                    drawable,
-                    registrationX,
-                    registrationY,
-                    initialDragViewScale,
-                    dragViewScaleOnDrop,
-                    scaleDps)
-                : new DragView(
-                    mLauncher,
-                    view,
-                    view.getMeasuredWidth(),
-                    view.getMeasuredHeight(),
-                    registrationX,
-                    registrationY,
-                    initialDragViewScale,
-                    dragViewScaleOnDrop,
-                    scaleDps);
-        dragView.setItemInfo(dragInfo);
-        mDragObject.dragComplete = false;
-
-        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
-        mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
-
-        mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
-        if (!mOptions.isAccessibleDrag) {
-            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
-        }
-
-        mDragObject.dragSource = source;
-        mDragObject.dragInfo = dragInfo;
-        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
-
-        if (dragOffset != null) {
-            dragView.setDragVisualizeOffset(new Point(dragOffset));
-        }
-        if (dragRegion != null) {
-            dragView.setDragRegion(new Rect(dragRegion));
-        }
-
-        mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        dragView.show(mLastTouch.x, mLastTouch.y);
-        mDistanceSinceScroll = 0;
-
-        if (!mIsInPreDrag) {
-            callOnDragStart();
-        } else if (mOptions.preDragCondition != null) {
-            mOptions.preDragCondition.onPreDragStart(mDragObject);
-        }
-
-        handleMoveEvent(mLastTouch.x, mLastTouch.y);
-
-        if (!mLauncher.isTouchInProgress() && options.simulatedDndStartPoint == null) {
-            // If it is an internal drag and the touch is already complete, cancel immediately
-            MAIN_EXECUTOR.submit(this::cancelDrag);
-        }
-        return dragView;
-    }
-
-    private void callOnDragStart() {
+    protected void callOnDragStart() {
         if (mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
@@ -357,13 +258,15 @@
         if (!accepted) {
             // If it was not accepted, cleanup the state. If it was accepted, it is the
             // responsibility of the drop target to cleanup the state.
-            mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+            exitDrag();
             mDragObject.deferDragViewCleanupPostAnimation = false;
         }
 
         mDragObject.dragSource.onDropCompleted(dropTarget, mDragObject, accepted);
     }
 
+    protected abstract void exitDrag();
+
     public void onAppsRemoved(ItemInfoMatcher matcher) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
@@ -377,7 +280,7 @@
         }
     }
 
-    private void endDrag() {
+    protected void endDrag() {
         if (isDragging()) {
             mDragDriver = null;
             boolean isDeferred = false;
@@ -396,8 +299,6 @@
                 callOnDragEnd();
             }
         }
-
-        mFlingToDeleteHelper.releaseVelocityTracker();
     }
 
     public void animateDragViewToOriginalPosition(final Runnable onComplete,
@@ -443,7 +344,7 @@
      * Clamps the position to the drag layer bounds.
      */
     private Point getClampedDragLayerPos(float x, float y) {
-        mLauncher.getDragLayer().getLocalVisibleRect(mRectTemp);
+        mActivity.getDragLayer().getLocalVisibleRect(mRectTemp);
         mTmpPoint.x = (int) Math.max(mRectTemp.left, Math.min(x, mRectTemp.right - 1));
         mTmpPoint.y = (int) Math.max(mRectTemp.top, Math.min(y, mRectTemp.bottom - 1));
         return mTmpPoint;
@@ -465,19 +366,16 @@
 
     @Override
     public void onDriverDragEnd(float x, float y) {
-        DropTarget dropTarget;
-        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject, mOptions);
-        if (flingAnimation != null) {
-            dropTarget = mFlingToDeleteHelper.getDropTarget();
-        } else {
-            dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
+        if (!endWithFlingAnimation()) {
+            drop(findDropTarget((int) x, (int) y, mCoordinatesTemp), null);
         }
-
-        drop(dropTarget, flingAnimation);
-
         endDrag();
     }
 
+    protected boolean endWithFlingAnimation() {
+        return false;
+    }
+
     @Override
     public void onDriverDragCancel() {
         cancelDrag();
@@ -520,7 +418,7 @@
         return mDragDriver != null && mDragDriver.onDragEvent(event);
     }
 
-    private void handleMoveEvent(int x, int y) {
+    protected void handleMoveEvent(int x, int y) {
         mDragObject.dragView.move(x, y);
 
         // Drop on someone?
@@ -592,7 +490,7 @@
         endDrag();
     }
 
-    private void drop(DropTarget dropTarget, Runnable flingAnimation) {
+    protected void drop(DropTarget dropTarget, Runnable flingAnimation) {
         final int[] coordinates = mCoordinatesTemp;
         mDragObject.x = coordinates[0];
         mDragObject.y = coordinates[1];
@@ -649,7 +547,7 @@
             if (r.contains(x, y)) {
                 dropCoordinates[0] = x;
                 dropCoordinates[1] = y;
-                mLauncher.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates);
+                mActivity.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates);
                 return target;
             }
         }
@@ -657,11 +555,11 @@
         // cell layout to drop to in the existing drag/drop logic.
         dropCoordinates[0] = x;
         dropCoordinates[1] = y;
-        mLauncher.getDragLayer().mapCoordInSelfToDescendant(mLauncher.getWorkspace(),
-                dropCoordinates);
-        return mLauncher.getWorkspace();
+        return getDefaultDropTarget(dropCoordinates);
     }
 
+    protected abstract DropTarget getDefaultDropTarget(int[] dropCoordinates);
+
     /**
      * Sets the drag listener which will be notified when a drag starts or ends.
      */
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index 0a1aba1..336fced 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -22,6 +22,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.ButtonDropTarget;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -35,15 +36,12 @@
     private static final float MAX_FLING_DEGREES = 35f;
 
     private final Launcher mLauncher;
-    private final int mFlingToDeleteThresholdVelocity;
 
     private ButtonDropTarget mDropTarget;
     private VelocityTracker mVelocityTracker;
 
     public FlingToDeleteHelper(Launcher launcher) {
         mLauncher = launcher;
-        mFlingToDeleteThresholdVelocity = launcher.getResources()
-                .getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
     }
 
     public void recordMotionEvent(MotionEvent ev) {
@@ -91,12 +89,13 @@
         mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
         PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
         float theta = MAX_FLING_DEGREES + 1;
-        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+        DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
+        if (mVelocityTracker.getYVelocity() < deviceProfile.flingToDeleteThresholdVelocity) {
             // Do a quick dot product test to ensure that we are flinging upwards
             PointF upVec = new PointF(0f, -1f);
             theta = getAngleBetweenVectors(vel, upVec);
         } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
-                mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
+                mVelocityTracker.getXVelocity() < deviceProfile.flingToDeleteThresholdVelocity) {
             // Remove icon is on left side instead of top, so check if we are flinging to the left.
             PointF leftVec = new PointF(-1f, 0f);
             theta = getAngleBetweenVectors(vel, leftVec);
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
new file mode 100644
index 0000000..a98d70c
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -0,0 +1,185 @@
+/*
+ * 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.dragndrop;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Drag controller for Launcher activity
+ */
+public class LauncherDragController extends DragController<Launcher> {
+
+    private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+    private final FlingToDeleteHelper mFlingToDeleteHelper;
+
+    public LauncherDragController(Launcher launcher) {
+        super(launcher);
+        mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
+    }
+
+    @Override
+    protected DragView startDrag(
+            @Nullable Drawable drawable,
+            @Nullable View view,
+            DraggableView originalView,
+            int dragLayerX,
+            int dragLayerY,
+            DragSource source,
+            ItemInfo dragInfo,
+            Point dragOffset,
+            Rect dragRegion,
+            float initialDragViewScale,
+            float dragViewScaleOnDrop,
+            DragOptions options) {
+        if (PROFILE_DRAWING_DURING_DRAG) {
+            android.os.Debug.startMethodTracing("Launcher");
+        }
+
+        mActivity.hideKeyboard();
+        AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_DISCOVERY_BOUNCE);
+
+        mOptions = options;
+        if (mOptions.simulatedDndStartPoint != null) {
+            mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+            mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
+        }
+
+        final int registrationX = mMotionDown.x - dragLayerX;
+        final int registrationY = mMotionDown.y - dragLayerY;
+
+        final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+        final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+        mLastDropTarget = null;
+
+        mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
+        mDragObject.originalView = originalView;
+
+        mIsInPreDrag = mOptions.preDragCondition != null
+                && !mOptions.preDragCondition.shouldStartDrag(0);
+
+        final Resources res = mActivity.getResources();
+        final float scaleDps = mIsInPreDrag
+                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+        final DragView dragView = mDragObject.dragView = drawable != null
+                ? new DragView(
+                mActivity,
+                drawable,
+                registrationX,
+                registrationY,
+                initialDragViewScale,
+                dragViewScaleOnDrop,
+                scaleDps)
+                : new DragView(
+                        mActivity,
+                        view,
+                        view.getMeasuredWidth(),
+                        view.getMeasuredHeight(),
+                        registrationX,
+                        registrationY,
+                        initialDragViewScale,
+                        dragViewScaleOnDrop,
+                        scaleDps);
+        dragView.setItemInfo(dragInfo);
+        mDragObject.dragComplete = false;
+
+        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+        mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+        mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
+        if (!mOptions.isAccessibleDrag) {
+            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+        }
+
+        mDragObject.dragSource = source;
+        mDragObject.dragInfo = dragInfo;
+        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+        if (dragOffset != null) {
+            dragView.setDragVisualizeOffset(new Point(dragOffset));
+        }
+        if (dragRegion != null) {
+            dragView.setDragRegion(new Rect(dragRegion));
+        }
+
+        mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        dragView.show(mLastTouch.x, mLastTouch.y);
+        mDistanceSinceScroll = 0;
+
+        if (!mIsInPreDrag) {
+            callOnDragStart();
+        } else if (mOptions.preDragCondition != null) {
+            mOptions.preDragCondition.onPreDragStart(mDragObject);
+        }
+
+        handleMoveEvent(mLastTouch.x, mLastTouch.y);
+
+        if (!mActivity.isTouchInProgress() && options.simulatedDndStartPoint == null) {
+            // If it is an internal drag and the touch is already complete, cancel immediately
+            MAIN_EXECUTOR.submit(this::cancelDrag);
+        }
+        return dragView;
+    }
+
+    @Override
+    protected void exitDrag() {
+        mActivity.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+    }
+
+    @Override
+    protected boolean endWithFlingAnimation() {
+        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject, mOptions);
+        if (flingAnimation != null) {
+            drop(mFlingToDeleteHelper.getDropTarget(), flingAnimation);
+            return true;
+        }
+        return super.endWithFlingAnimation();
+    }
+
+    @Override
+    protected void endDrag() {
+        super.endDrag();
+        mFlingToDeleteHelper.releaseVelocityTracker();
+    }
+
+    @Override
+    protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+        mActivity.getDragLayer().mapCoordInSelfToDescendant(mActivity.getWorkspace(),
+                dropCoordinates);
+        return mActivity.getWorkspace();
+    }
+}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 92d891f..22d1b1c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -23,7 +23,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
@@ -109,7 +108,6 @@
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -177,8 +175,9 @@
     private boolean mIsAnimatingClosed = false;
 
     // Folder can be displayed in Launcher's activity or a separate window (e.g. Taskbar).
-    // Anything specific to Launcher should use mLauncher, otherwise should use mActivityContext.
-    protected final Launcher mLauncher;
+    // Anything specific to Launcher should use mLauncherDelegate, otherwise should
+    // use mActivityContext.
+    protected final LauncherDelegate mLauncherDelegate;
     protected final ActivityContext mActivityContext;
 
     protected DragController mDragController;
@@ -235,8 +234,6 @@
     // Wallpaper local color extraction
     @Nullable private LocalColorExtractor mColorExtractor;
     @Nullable private LocalColorExtractor.Listener mColorListener;
-    private final Rect mTempRect = new Rect();
-    private final RectF mTempRectF = new RectF();
 
     // For simplicity, we start the color change only after the open animation has started.
     private Runnable mColorChangeRunnable;
@@ -255,8 +252,9 @@
         super(context, attrs);
         setAlwaysDrawnWithCacheEnabled(false);
 
-        mLauncher = Launcher.getLauncher(context);
         mActivityContext = ActivityContext.lookupContext(context);
+        mLauncherDelegate = LauncherDelegate.from(mActivityContext);
+
         mStatsLogManager = StatsLogManager.newInstance(context);
         // We need this view to be focusable in touch mode so that when text editing of the folder
         // name is complete, we have something to focus on, thus hiding the cursor and giving
@@ -267,7 +265,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        final DeviceProfile dp = mLauncher.getDeviceProfile();
+        final DeviceProfile dp = mActivityContext.getDeviceProfile();
         final int paddingLeftRight = dp.folderContentPaddingLeftRight;
 
         mContent = findViewById(R.id.folder_content);
@@ -298,7 +296,7 @@
         }
 
         if (Utilities.ATLEAST_S) {
-            mColorExtractor = LocalColorExtractor.newInstance(mLauncher);
+            mColorExtractor = LocalColorExtractor.newInstance(getContext());
             mColorListener = (RectF rect, SparseIntArray extractedColors) -> {
                 mColorChangeRunnable = () -> {
                     mColorChangeRunnable = null;
@@ -339,7 +337,7 @@
 
     public boolean onLongClick(View v) {
         // Return if global dragging is not enabled
-        if (!mLauncher.isDraggingEnabled()) return true;
+        if (!mLauncherDelegate.isDraggingEnabled()) return true;
         return startDrag(v, new DragOptions());
     }
 
@@ -365,7 +363,7 @@
                         });
             }
 
-            mLauncher.getWorkspace().beginDragShared(v, this, options);
+            mLauncherDelegate.beginDragShared(v, this, options);
         }
         return true;
     }
@@ -421,7 +419,7 @@
         if (DEBUG) {
             Log.d(TAG, "onBackKey newTitle=" + newTitle);
         }
-        mInfo.setTitle(newTitle, mLauncher.getModelWriter());
+        mInfo.setTitle(newTitle, mLauncherDelegate.getModelWriter());
         mFolderIcon.onTitleChanged(newTitle);
 
         if (TextUtils.isEmpty(mInfo.title)) {
@@ -486,6 +484,7 @@
 
     public void setFolderIcon(FolderIcon icon) {
         mFolderIcon = icon;
+        mLauncherDelegate.init(this, icon);
     }
 
     @Override
@@ -592,8 +591,8 @@
     }
 
     private void startAnimation(final AnimatorSet a) {
-        mLauncher.getWorkspace().getVisiblePages()
-                .forEach(visiblePage -> addAnimatorListenerForPage(a, (CellLayout) visiblePage));
+        mLauncherDelegate.forEachVisibleWorkspacePage(
+                visiblePage -> addAnimatorListenerForPage(a, (CellLayout) visiblePage));
 
         a.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -754,12 +753,12 @@
                     mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
                         .translationX(0)
                         .setInterpolator(AnimationUtils.loadInterpolator(
-                                mLauncher, android.R.interpolator.fast_out_slow_in));
+                                getContext(), android.R.interpolator.fast_out_slow_in));
                     mPageIndicator.playEntryAnimation();
 
                     if (updateAnimationFlag) {
                         mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true,
-                                mLauncher.getModelWriter());
+                                mLauncherDelegate.getModelWriter());
                     }
                 }
             });
@@ -1115,7 +1114,7 @@
         if (getItemCount() <= mContent.itemsPerPage()) {
             // Show the animation, next time something is added to the folder.
             mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false,
-                    mLauncher.getModelWriter());
+                    mLauncherDelegate.getModelWriter());
         }
     }
 
@@ -1133,7 +1132,7 @@
         }
 
         if (!items.isEmpty()) {
-            mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+            mLauncherDelegate.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
         }
         if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind
                 && total > 1 /* no need to update if there's one icon */) {
@@ -1190,12 +1189,7 @@
         if (mColorExtractor != null) {
             mColorExtractor.removeLocations();
             mColorExtractor.setListener(mColorListener);
-            mTempRect.set(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
-            mColorExtractor.getExtractedRectForViewRect(mLauncher,
-                    mLauncher.getWorkspace().getCurrentPage(), mTempRect, mTempRectF);
-            if (!mTempRectF.isEmpty()) {
-                mColorExtractor.addLocation(Arrays.asList(mTempRectF));
-            }
+            mLauncherDelegate.addRectForColorExtraction(lp, mColorExtractor);
         }
     }
 
@@ -1236,7 +1230,7 @@
 
         if (mContent.getChildCount() > 0) {
             int cellIconGap = (mContent.getPageAt(0).getCellWidth()
-                    - mLauncher.getDeviceProfile().iconSizePx) / 2;
+                    - mActivityContext.getDeviceProfile().iconSizePx) / 2;
             mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap,
                     mFooter.getPaddingTop(),
                     mContent.getPaddingRight() + cellIconGap,
@@ -1266,56 +1260,7 @@
     }
 
     @Thunk void replaceFolderWithFinalItem() {
-        // Add the last remaining child to the workspace in place of the folder
-        Runnable onCompleteRunnable = new Runnable() {
-            @Override
-            public void run() {
-                int itemCount = getItemCount();
-                if (itemCount <= 1) {
-                    View newIcon = null;
-                    WorkspaceItemInfo finalItem = null;
-
-                    if (itemCount == 1) {
-                        // Move the item from the folder to the workspace, in the position of the
-                        // folder
-                        CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
-                                mInfo.screenId);
-                        finalItem =  mInfo.contents.remove(0);
-                        newIcon = mLauncher.createShortcut(cellLayout, finalItem);
-                        mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
-                                mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
-                    }
-
-                    // Remove the folder
-                    mLauncher.removeItem(mFolderIcon, mInfo, true /* deleteFromDb */);
-                    if (mFolderIcon instanceof DropTarget) {
-                        mDragController.removeDropTarget((DropTarget) mFolderIcon);
-                    }
-
-                    if (newIcon != null) {
-                        // We add the child after removing the folder to prevent both from existing
-                        // at the same time in the CellLayout.  We need to add the new item with
-                        // addInScreenFromBind() to ensure that hotseat items are placed correctly.
-                        mLauncher.getWorkspace().addInScreenFromBind(newIcon, mInfo);
-
-                        // Focus the newly created child
-                        newIcon.requestFocus();
-                    }
-                    if (finalItem != null) {
-                        StatsLogger logger = mStatsLogManager.logger().withItemInfo(finalItem);
-                        mDragController.getLogInstanceId().map(logger::withInstanceId)
-                                .orElse(logger)
-                                .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
-                    }
-                }
-            }
-        };
-        View finalChild = mContent.getLastItem();
-        if (finalChild != null) {
-            mFolderIcon.performDestroyAnimation(onCompleteRunnable);
-        } else {
-            onCompleteRunnable.run();
-        }
+        mLauncherDelegate.replaceFolderWithFinalItem(this);
         mDestroyed = true;
     }
 
@@ -1374,6 +1319,10 @@
             mScrollPauseAlarm.cancelAlarm();
         }
         mContent.completePendingPageChanges();
+        Launcher launcher = mLauncherDelegate.getLauncher();
+        if (launcher == null) {
+            return;
+        }
 
         PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
                 ? (PendingAddShortcutInfo) d.dragInfo : null;
@@ -1383,7 +1332,7 @@
             pasi.container = mInfo.id;
             pasi.rank = mEmptyCellRank;
 
-            mLauncher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
+            launcher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
                     pasi.spanY);
             d.deferDragViewCleanupPostAnimation = false;
             mRearrangeOnClose = true;
@@ -1405,7 +1354,7 @@
 
                 // Actually move the item in the database if it was an external drag. Call this
                 // before creating the view, so that WorkspaceItemInfo is updated appropriately.
-                mLauncher.getModelWriter().addOrMoveItemInDatabase(
+                mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(
                         si, mInfo.id, 0, si.cellX, si.cellY);
                 mIsExternalDrag = false;
             } else {
@@ -1420,7 +1369,7 @@
                 float scaleY = getScaleY();
                 setScaleX(1.0f);
                 setScaleY(1.0f);
-                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, null);
+                launcher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, null);
                 setScaleX(scaleX);
                 setScaleY(scaleY);
             } else {
@@ -1448,10 +1397,11 @@
 
         if (mContent.getPageCount() > 1) {
             // The animation has already been shown while opening the folder.
-            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter());
+            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true,
+                    mLauncherDelegate.getModelWriter());
         }
 
-        mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+        launcher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
@@ -1480,7 +1430,7 @@
         FolderGridOrganizer verifier = new FolderGridOrganizer(
                 mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo);
         verifier.updateRankAndPos(item, rank);
-        mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
+        mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
                 item.cellY);
         updateItemLocationsInDatabaseBatch(false);
 
@@ -1713,17 +1663,9 @@
                     return true;
                 }
                 return false;
-            } else if (!dl.isEventOverView(this, ev)) {
-                if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
-                    // Do not close the container if in drag and drop.
-                    if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
-                        return true;
-                    }
-                } else {
-                    // TODO: add ww log if need to gather tap outside to close folder
-                    close(true);
-                    return true;
-                }
+            } else if (!dl.isEventOverView(this, ev)
+                    && mLauncherDelegate.interceptOutsideTouch(ev, dl, this)) {
+                return true;
             }
         }
         return false;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6b02021..279c445 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -169,14 +169,11 @@
     public static <T extends Context & ActivityContext> FolderIcon inflateFolderAndIcon(int resId,
             T activityContext, ViewGroup group, FolderInfo folderInfo) {
         Folder folder = Folder.fromXml(activityContext);
-        folder.setDragController(folder.mLauncher.getDragController());
 
         FolderIcon icon = inflateIcon(resId, activityContext, group, folderInfo);
         folder.setFolderIcon(icon);
         folder.bind(folderInfo);
         icon.setFolder(folder);
-
-        icon.setOnFocusChangeListener(folder.mLauncher.getFocusHandler());
         icon.mBackground.paddingY = icon.isInHotseat()
                 ? 0 : activityContext.getDeviceProfile().cellYPaddingPx;
         return icon;
@@ -467,7 +464,7 @@
         CharSequence newTitle = nameInfos.getLabels()[0];
         FromState fromState = mInfo.getFromLabelState();
 
-        mInfo.setTitle(newTitle, mFolder.mLauncher.getModelWriter());
+        mInfo.setTitle(newTitle, mFolder.mLauncherDelegate.getModelWriter());
         onTitleChanged(mInfo.title);
         mFolder.mFolderName.setText(mInfo.title);
 
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index a4e8be6..7fc3740 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -31,7 +31,6 @@
 import android.view.ViewDebug;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
@@ -49,6 +48,7 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -102,7 +102,7 @@
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
 
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
-        mViewCache = BaseActivity.fromContext(context).getViewCache();
+        mViewCache = ActivityContext.lookupContext(context).getViewCache();
     }
 
     public void setFolder(Folder folder) {
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
new file mode 100644
index 0000000..f7d8e8c
--- /dev/null
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -0,0 +1,225 @@
+/*
+ * 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.folder;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.BaseDragLayer.LayoutParams;
+import com.android.launcher3.widget.LocalColorExtractor;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Wrapper around Launcher methods to allow folders in non-launcher context
+ */
+public class LauncherDelegate {
+
+    private final Launcher mLauncher;
+    private final Rect mTempRect = new Rect();
+    private final RectF mTempRectF = new RectF();
+
+    private LauncherDelegate(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    void init(Folder folder, FolderIcon icon) {
+        folder.setDragController(mLauncher.getDragController());
+        icon.setOnFocusChangeListener(mLauncher.getFocusHandler());
+    }
+
+    boolean isDraggingEnabled() {
+        return mLauncher.isDraggingEnabled();
+    }
+
+    void beginDragShared(View child, DragSource source, DragOptions options) {
+        mLauncher.getWorkspace().beginDragShared(child, source, options);
+    }
+
+    ModelWriter getModelWriter() {
+        return mLauncher.getModelWriter();
+    }
+
+    void forEachVisibleWorkspacePage(Consumer<View> callback) {
+        mLauncher.getWorkspace().forEachVisiblePage(callback);
+    }
+
+    @Nullable
+    Launcher getLauncher() {
+        return mLauncher;
+    }
+
+    void addRectForColorExtraction(BaseDragLayer.LayoutParams lp, LocalColorExtractor target) {
+        mTempRect.set(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
+        target.getExtractedRectForViewRect(mLauncher,
+                mLauncher.getWorkspace().getCurrentPage(), mTempRect, mTempRectF);
+        if (!mTempRectF.isEmpty()) {
+            target.addLocation(Arrays.asList(mTempRectF));
+        }
+    }
+
+    void replaceFolderWithFinalItem(Folder folder) {
+        // Add the last remaining child to the workspace in place of the folder
+        Runnable onCompleteRunnable = new Runnable() {
+            @Override
+            public void run() {
+                int itemCount = folder.getItemCount();
+                FolderInfo info = folder.mInfo;
+                if (itemCount <= 1) {
+                    View newIcon = null;
+                    WorkspaceItemInfo finalItem = null;
+
+                    if (itemCount == 1) {
+                        // Move the item from the folder to the workspace, in the position of the
+                        // folder
+                        CellLayout cellLayout = mLauncher.getCellLayout(info.container,
+                                info.screenId);
+                        finalItem =  info.contents.remove(0);
+                        newIcon = mLauncher.createShortcut(cellLayout, finalItem);
+                        mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
+                                info.container, info.screenId, info.cellX, info.cellY);
+                    }
+
+                    // Remove the folder
+                    mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */);
+                    if (folder.mFolderIcon instanceof DropTarget) {
+                        folder.mDragController.removeDropTarget((DropTarget) folder.mFolderIcon);
+                    }
+
+                    if (newIcon != null) {
+                        // We add the child after removing the folder to prevent both from existing
+                        // at the same time in the CellLayout.  We need to add the new item with
+                        // addInScreenFromBind() to ensure that hotseat items are placed correctly.
+                        mLauncher.getWorkspace().addInScreenFromBind(newIcon, info);
+
+                        // Focus the newly created child
+                        newIcon.requestFocus();
+                    }
+                    if (finalItem != null) {
+                        StatsLogger logger = mLauncher.getStatsLogManager().logger()
+                                .withItemInfo(finalItem);
+                        ((Optional<InstanceId>) folder.mDragController.getLogInstanceId())
+                                .map(logger::withInstanceId)
+                                .orElse(logger)
+                                .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
+                    }
+                }
+            }
+        };
+        View finalChild = folder.mContent.getLastItem();
+        if (finalChild != null) {
+            folder.mFolderIcon.performDestroyAnimation(onCompleteRunnable);
+        } else {
+            onCompleteRunnable.run();
+        }
+    }
+
+
+    boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
+        if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
+            // Do not close the container if in drag and drop.
+            if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
+                return true;
+            }
+        } else {
+            // TODO: add ww log if need to gather tap outside to close folder
+            folder.close(true);
+            return true;
+        }
+        return false;
+    }
+
+    private static class FallbackDelegate extends LauncherDelegate {
+
+        private final ActivityContext mContext;
+        private ModelWriter mWriter;
+
+        FallbackDelegate(ActivityContext context) {
+            super(null);
+            mContext = context;
+        }
+
+        @Override
+        void init(Folder folder, FolderIcon icon) {
+            folder.setDragController(mContext.getDragController());
+        }
+
+        @Override
+        boolean isDraggingEnabled() {
+            return false;
+        }
+
+        @Override
+        void beginDragShared(View child, DragSource source, DragOptions options) { }
+
+        @Override
+        ModelWriter getModelWriter() {
+            if (mWriter == null) {
+                mWriter = LauncherAppState.getInstance((Context) mContext).getModel()
+                        .getWriter(false, false);
+            }
+            return mWriter;
+        }
+
+        @Override
+        void forEachVisibleWorkspacePage(Consumer<View> callback) { }
+
+        @Override
+        Launcher getLauncher() {
+            return null;
+        }
+
+        @Override
+        void replaceFolderWithFinalItem(Folder folder) { }
+
+        @Override
+        boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
+            folder.close(true);
+            return true;
+        }
+
+        @Override
+        void addRectForColorExtraction(LayoutParams lp, LocalColorExtractor target) { }
+    }
+
+    static LauncherDelegate from(ActivityContext context) {
+        return context instanceof Launcher
+                ? new LauncherDelegate((Launcher) context)
+                : new FallbackDelegate(context);
+    }
+}
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index 0eea92c..b3057d5 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -94,11 +94,6 @@
                     "SearchActionItemInfo can only have either an Intent or a PendingIntent");
         }
         mIntent = intent;
-        // bandage fix for just one week
-        if (intent != null && "com.android.server.telecom".equals(intent.getPackage())) {
-            intent.setAction(Intent.ACTION_DIAL);
-            intent.setPackage(null);
-        }
     }
 
     public PendingIntent getPendingIntent() {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 03351af..73ac8f2 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -179,7 +179,7 @@
                         (int) argb.evaluate((i + 1) * step, primaryColor, secondaryColor);
             }
 
-            if (Utilities.ATLEAST_S) {
+            if (supportsColorExtraction()) {
                 setupColorExtraction();
             }
         }
@@ -676,6 +676,13 @@
     }
 
     /**
+     * Returns whether color extraction is supported.
+     */
+    public boolean supportsColorExtraction() {
+        return Utilities.ATLEAST_S;
+    }
+
+    /**
      * Callback to be called when the popup is closed
      */
     public void setOnCloseCallback(@NonNull Runnable callback) {
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 216510b..883ff75 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -19,7 +19,6 @@
 import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
 
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
 
 import android.content.SharedPreferences;
 import android.os.Bundle;
@@ -38,6 +37,8 @@
 import androidx.preference.PreferenceScreen;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -169,12 +170,14 @@
                     return !WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS;
 
                 case ALLOW_ROTATION_PREFERENCE_KEY:
-                    if (getResources().getBoolean(R.bool.allow_rotation)) {
+                    DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
+                            getContext()).getDeviceProfile(getContext());
+                    if (deviceProfile.allowRotation) {
                         // Launcher supports rotation by default. No need to show this setting.
                         return false;
                     }
                     // Initialize the UI once
-                    preference.setDefaultValue(getAllowRotationDefaultValue());
+                    preference.setDefaultValue(false);
                     return true;
 
                 case FLAGS_PREFERENCE_KEY:
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 14ef2dc..03b6853 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -129,14 +129,14 @@
     }
 
     /**
-     * @see #goToState(STATE_TYPE, boolean, Runnable)
+     * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
      */
     public void goToState(STATE_TYPE state) {
         goToState(state, shouldAnimateStateChange());
     }
 
     /**
-     * @see #goToState(STATE_TYPE, boolean, Runnable)
+     * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
      */
     public void goToState(STATE_TYPE state, boolean animated) {
         goToState(state, animated, 0, null);
@@ -149,15 +149,15 @@
      *                true otherwise
      * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
      */
-    public void goToState(STATE_TYPE state, boolean animated, Runnable onCompleteRunnable) {
-        goToState(state, animated, 0, onCompleteRunnable);
+    public void goToState(STATE_TYPE state, boolean animated, AnimatorListener listener) {
+        goToState(state, animated, 0, listener);
     }
 
     /**
      * Changes the Launcher state to the provided state after the given delay.
      */
-    public void goToState(STATE_TYPE state, long delay, Runnable onCompleteRunnable) {
-        goToState(state, true, delay, onCompleteRunnable);
+    public void goToState(STATE_TYPE state, long delay, AnimatorListener listener) {
+        goToState(state, true, delay, listener);
     }
 
     /**
@@ -187,21 +187,20 @@
         }
     }
 
-    private void goToState(STATE_TYPE state, boolean animated, long delay,
-            final Runnable onCompleteRunnable) {
+    private void goToState(
+            STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
         animated &= areAnimatorsEnabled();
         if (mActivity.isInState(state)) {
             if (mConfig.currentAnimation == null) {
                 // Run any queued runnable
-                if (onCompleteRunnable != null) {
-                    onCompleteRunnable.run();
+                if (listener != null) {
+                    listener.onAnimationEnd(null);
                 }
                 return;
             } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
                 // We are running the same animation as requested
-                if (onCompleteRunnable != null) {
-                    mConfig.currentAnimation.addListener(
-                            AnimationSuccessListener.forRunnable(onCompleteRunnable));
+                if (listener != null) {
+                    mConfig.currentAnimation.addListener(listener);
                 }
                 return;
             }
@@ -221,8 +220,8 @@
             onStateTransitionEnd(state);
 
             // Run any queued runnable
-            if (onCompleteRunnable != null) {
-                onCompleteRunnable.run();
+            if (listener != null) {
+                listener.onAnimationEnd(null);
             }
             return;
         }
@@ -233,16 +232,16 @@
             int startChangeId = mConfig.changeId;
             mUiHandler.postDelayed(() -> {
                 if (mConfig.changeId == startChangeId) {
-                    goToStateAnimated(state, fromState, onCompleteRunnable);
+                    goToStateAnimated(state, fromState, listener);
                 }
             }, delay);
         } else {
-            goToStateAnimated(state, fromState, onCompleteRunnable);
+            goToStateAnimated(state, fromState, listener);
         }
     }
 
     private void goToStateAnimated(STATE_TYPE state, STATE_TYPE fromState,
-            Runnable onCompleteRunnable) {
+            AnimatorListener listener) {
         // Since state mBaseState can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
         mConfig.duration = state == mBaseState
@@ -250,8 +249,8 @@
                 : state.getTransitionDuration(mActivity);
         prepareForAtomicAnimation(fromState, state, mConfig);
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
-        if (onCompleteRunnable != null) {
-            animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
+        if (listener != null) {
+            animation.addListener(listener);
         }
         mUiHandler.post(new StartAnimRunnable(animation));
     }
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 2da06e9..5832711 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,43 +18,34 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 
-import android.app.Activity;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Resources;
 
-import com.android.launcher3.R;
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.UiThreadHelper;
 
 /**
  * Utility class to manage launcher rotation
  */
-public class RotationHelper implements OnSharedPreferenceChangeListener {
+public class RotationHelper implements OnSharedPreferenceChangeListener,
+        DeviceProfile.OnDeviceProfileChangeListener {
 
     private static final String TAG = "RotationHelper";
 
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
-    public static boolean getAllowRotationDefaultValue() {
-        // If the device's pixel density was scaled (usually via settings for A11y), use the
-        // original dimensions to determine if rotation is allowed of not.
-        Resources res = Resources.getSystem();
-        int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
-                * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
-        return originalSmallestWidth >= 600;
-    }
-
     public static final int REQUEST_NONE = 0;
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
 
-    private Activity mActivity;
-    private final SharedPreferences mSharedPrefs;
+    private BaseActivity mActivity;
+    private SharedPreferences mSharedPrefs = null;
 
     private boolean mIgnoreAutoRotateSettings;
+    private boolean mForceAllowRotationForTesting;
     private boolean mHomeRotationEnabled;
 
     /**
@@ -79,18 +70,25 @@
     // Initialize mLastActivityFlags to a value not used by SCREEN_ORIENTATION flags
     private int mLastActivityFlags = -999;
 
-    public RotationHelper(Activity activity) {
+    public RotationHelper(BaseActivity activity) {
         mActivity = activity;
+    }
 
+    private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) {
         // On large devices we do not handle auto-rotate differently.
-        mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
+        mIgnoreAutoRotateSettings = ignoreAutoRotateSettings;
         if (!mIgnoreAutoRotateSettings) {
-            mSharedPrefs = Utilities.getPrefs(mActivity);
-            mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+            if (mSharedPrefs == null) {
+                mSharedPrefs = Utilities.getPrefs(mActivity);
+                mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+            }
             mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                    getAllowRotationDefaultValue());
+                    mActivity.getDeviceProfile().allowRotation);
         } else {
-            mSharedPrefs = null;
+            if (mSharedPrefs != null) {
+                mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+                mSharedPrefs = null;
+            }
         }
     }
 
@@ -99,12 +97,21 @@
         if (mDestroyed) return;
         boolean wasRotationEnabled = mHomeRotationEnabled;
         mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                getAllowRotationDefaultValue());
+                mActivity.getDeviceProfile().allowRotation);
         if (mHomeRotationEnabled != wasRotationEnabled) {
             notifyChange();
         }
     }
 
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        boolean ignoreAutoRotateSettings = dp.allowRotation;
+        if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
+            setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
+            notifyChange();
+        }
+    }
+
     public void setStateHandlerRequest(int request) {
         if (mStateHandlerRequest != request) {
             mStateHandlerRequest = request;
@@ -128,14 +135,15 @@
 
     // Used by tests only.
     public void forceAllowRotationForTesting(boolean allowRotation) {
-        mIgnoreAutoRotateSettings =
-                allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation);
+        mForceAllowRotationForTesting = allowRotation;
         notifyChange();
     }
 
     public void initialize() {
         if (!mInitialized) {
             mInitialized = true;
+            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().allowRotation);
+            mActivity.addOnDeviceProfileChangeListener(this);
             notifyChange();
         }
     }
@@ -143,6 +151,7 @@
     public void destroy() {
         if (!mDestroyed) {
             mDestroyed = true;
+            mActivity.removeOnDeviceProfileChangeListener(this);
             mActivity = null;
             if (mSharedPrefs != null) {
                 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
@@ -165,7 +174,7 @@
         } else if (mCurrentStateRequest == REQUEST_LOCK) {
             activityFlags = SCREEN_ORIENTATION_LOCKED;
         } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
-                || mHomeRotationEnabled) {
+                || mHomeRotationEnabled || mForceAllowRotationForTesting) {
             activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
         } else {
             // If auto rotation is off, allow rotation on the activity, in case the user is using
@@ -191,9 +200,10 @@
 
     @Override
     public String toString() {
-        return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
-                + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]",
+        return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d, "
+                        + "mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, "
+                        + "mHomeRotationEnabled=%b, mForceAllowRotationForTesting=%b]",
                 mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
-                mIgnoreAutoRotateSettings, mHomeRotationEnabled);
+                mIgnoreAutoRotateSettings, mHomeRotationEnabled, mForceAllowRotationForTesting);
     }
 }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index d8e5a48..6811438 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -105,7 +105,7 @@
     int getPrimaryValue(int x, int y);
     int getSecondaryValue(int x, int y);
 
-    float  getPrimaryValue(float x, float y);
+    float getPrimaryValue(float x, float y);
     float getSecondaryValue(float x, float y);
 
     boolean isLayoutNaturalToLauncher();
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index f5e1234..523f3d6 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -23,11 +23,12 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.view.View;
 import android.view.WindowInsets;
 import android.view.inputmethod.InputMethodManager;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * Utility class for offloading some class from UI thread
@@ -43,21 +44,21 @@
     private static final int MSG_RUN_COMMAND = 3;
 
     @SuppressLint("NewApi")
-    public static void hideKeyboardAsync(Launcher launcher, IBinder token) {
+    public static void hideKeyboardAsync(ActivityContext activityContext, IBinder token) {
+        View root = activityContext.getDragLayer();
         if (Utilities.ATLEAST_R) {
-            WindowInsets rootInsets = launcher.getRootView().getRootWindowInsets();
+            WindowInsets rootInsets = root.getRootWindowInsets();
             boolean isImeShown = rootInsets != null && rootInsets.isVisible(
                     WindowInsets.Type.ime());
             if (isImeShown) {
                 // this call is already asynchronous
-                launcher.getAppsView().getWindowInsetsController().hide(
-                        WindowInsets.Type.ime()
-                );
+                root.getWindowInsetsController().hide(WindowInsets.Type.ime());
             }
             return;
         }
 
-        Message.obtain(HANDLER.get(launcher), MSG_HIDE_KEYBOARD, token).sendToTarget();
+        Message.obtain(HANDLER.get(root.getContext()),
+                MSG_HIDE_KEYBOARD, token).sendToTarget();
     }
 
     public static void setOrientationAsync(Activity activity, int orientation) {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 71aa4ac..646b669 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -23,7 +23,9 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ViewCache;
 
 /**
  * An interface to be used along with a context for various activities in Launcher. This allows a
@@ -86,6 +88,17 @@
 
     DeviceProfile getDeviceProfile();
 
+    default ViewCache getViewCache() {
+        return new ViewCache();
+    }
+
+    /**
+     * Controller for supporting item drag-and-drop
+     */
+    default <T extends DragController> T getDragController() {
+        return null;
+    }
+
     /**
      * Returns the ActivityContext associated with the given Context.
      */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 1524ab3..c3eda13 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -47,7 +47,6 @@
 
     private int mMaxSpansPerRow = 4;
     private final LayoutInflater mLayoutInflater;
-    private final int mIndent;
     private final OnClickListener mIconClickListener;
     private final OnLongClickListener mIconLongClickListener;
     private final WidgetPreviewLoader mWidgetPreviewLoader;
@@ -62,7 +61,6 @@
             WidgetPreviewLoader widgetPreviewLoader,
             WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
-        mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mWidgetPreviewLoader = widgetPreviewLoader;
@@ -90,11 +88,6 @@
 
         ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
                 R.layout.widgets_table_container, parent, false);
-
-        // if the end padding is 0, then container view (horizontal scroll view) doesn't respect
-        // the end of the linear layout width + the start padding and doesn't allow scrolling.
-        container.findViewById(R.id.widgets_table).setPaddingRelative(mIndent, 0, 1, 0);
-
         return new WidgetsRowViewHolder(container);
     }
 
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 7f6c8f8..918ec4a 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -109,7 +109,7 @@
         <activity
             android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
             android:clearTaskOnLaunch="true"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
             android:enabled="false"
             android:label="Test launcher"
             android:launchMode="singleTask"
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 2a0f7bb..8936f39 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -249,6 +249,7 @@
 
     @Before
     public void setUp() throws Exception {
+        mLauncher.onTestStart();
         Assert.assertTrue("Keyguard is visible, which is likely caused by a crash in SysUI",
                 TestHelpers.wait(
                         Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
@@ -288,13 +289,17 @@
 
     @After
     public void verifyLauncherState() {
-        // Limits UI tests affecting tests running after them.
-        mLauncher.waitForLauncherInitialized();
-        if (mLauncherPid != 0) {
-            assertEquals("Launcher crashed, pid mismatch:",
-                    mLauncherPid, mLauncher.getPid().intValue());
+        try {
+            // Limits UI tests affecting tests running after them.
+            mLauncher.waitForLauncherInitialized();
+            if (mLauncherPid != 0) {
+                assertEquals("Launcher crashed, pid mismatch:",
+                        mLauncherPid, mLauncher.getPid().intValue());
+            }
+            checkDetectedLeaks(mLauncher);
+        } finally {
+            mLauncher.onTestFinish();
         }
-        checkDetectedLeaks(mLauncher);
     }
 
     protected void clearLauncherData() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index e5b93b1..c77583c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -95,7 +95,6 @@
     private static final String TAG = "Tapl";
     private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
     private static final int GESTURE_STEP_MS = 16;
-    private static long START_TIME = System.currentTimeMillis();
 
     private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
     private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
@@ -107,6 +106,7 @@
     static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
     private final String mLauncherPackage;
     private final boolean mIsLauncher3;
+    private long mTestStartTime = -1;
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -422,6 +422,14 @@
         mOnSettledStateAction = onSettledStateAction;
     }
 
+    public void onTestStart() {
+        mTestStartTime = System.currentTimeMillis();
+    }
+
+    public void onTestFinish() {
+        mTestStartTime = -1;
+    }
+
     private String formatSystemHealthMessage(String message) {
         final String testPackage = getContext().getPackageName();
 
@@ -430,14 +438,16 @@
         mInstrumentation.getUiAutomation().grantRuntimePermission(
                 testPackage, "android.permission.PACKAGE_USAGE_STATS");
 
-        final String systemHealth = mSystemHealthSupplier != null
-                ? mSystemHealthSupplier.apply(START_TIME)
-                : TestHelpers.getSystemHealthMessage(getContext(), START_TIME);
+        if (mTestStartTime > 0) {
+            final String systemHealth = mSystemHealthSupplier != null
+                    ? mSystemHealthSupplier.apply(mTestStartTime)
+                    : TestHelpers.getSystemHealthMessage(getContext(), mTestStartTime);
 
-        if (systemHealth != null) {
-            return message
-                    + ",\nperhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
-                    + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
+            if (systemHealth != null) {
+                return message
+                        + ",\nperhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+                        + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
+            }
         }
 
         return message;
@@ -1349,6 +1359,8 @@
     }
 
     public int getTotalPssKb() {
+        // GC the system & sysui first before gc'ing launcher
+        logShellCommand("cmd statusbar run-gc");
         return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB).
                 getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }