Merge "Register hinge angle sensor listener in a background thread" into tm-dev
diff --git a/go/quickstep/res/values-or/strings.xml b/go/quickstep/res/values-or/strings.xml
index 36204a3..2e76e2d 100644
--- a/go/quickstep/res/values-or/strings.xml
+++ b/go/quickstep/res/values-or/strings.xml
@@ -7,13 +7,13 @@
     <string name="action_search" msgid="6269564710943755464">"Lens"</string>
     <string name="dialog_acknowledge" msgid="2804025517675853172">"ବୁଝିଗଲି"</string>
     <string name="dialog_cancel" msgid="6464336969134856366">"ବାତିଲ୍ କରନ୍ତୁ"</string>
-    <string name="dialog_settings" msgid="6564397136021186148">"ସେଟିଂସ୍"</string>
+    <string name="dialog_settings" msgid="6564397136021186148">"ସେଟିଂସ"</string>
     <string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ସ୍କିନରେ ଥିବା ଟେକ୍ସଟକୁ ଅନୁବାଦ କରନ୍ତୁ କିମ୍ବା ଶୁଣନ୍ତୁ"</string>
-    <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଟେକ୍ସଟ୍, ୱେବ୍ ଠିକଣା ଏବଂ ସ୍କ୍ରିନସଟଗୁଡ଼ିକ ପରି ସୂଚନାକୁ Google ସହ ସେୟାର୍ କରାଯାଇପାରେ।\n\nଆପଣ କେଉଁ ସୂଚନା ସେୟାର୍ କରନ୍ତି ତାହା ପରିବର୍ତ୍ତନ କରିବାକୁ, "<b>"ସେଟିଂସ୍ &gt; ଆପ୍ସ &gt; ଡିଫଲ୍ଟ ଆପ୍ସ &gt; ଡିଜିଟାଲ୍ ଆସିଷ୍ଟାଣ୍ଟ ଆପ"</b>"କୁ ଯାଆନ୍ତୁ।"</string>
+    <string name="niu_actions_confirmation_text" msgid="2105271481950866089">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଟେକ୍ସଟ, ୱେବ ଠିକଣା ଏବଂ ସ୍କ୍ରିନସଟଗୁଡ଼ିକ ପରି ସୂଚନାକୁ Google ସହ ସେୟାର କରାଯାଇପାରେ।\n\nଆପଣ କେଉଁ ସୂଚନାକୁ ସେୟାର କରନ୍ତି ତାହା ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, "<b>"ସେଟିଂସ &gt; ଆପ୍ସ &gt; ଡିଫଲ୍ଟ ଆପ୍ସ &gt; Digital assistant ଆପ"</b>"କୁ ଯାଆନ୍ତୁ।"</string>
     <string name="assistant_not_selected_title" msgid="5017072974603345228">"ଏହି ଫିଚର୍ ବ୍ୟବହାର କରିବାକୁ ଏକ ଆସିଷ୍ଟାଣ୍ଟ ବାଛନ୍ତୁ"</string>
-    <string name="assistant_not_selected_text" msgid="3244613673884359276">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା ପାଇଁ କିମ୍ବା ଅନୁବାଦ କରିବାକୁ, ସେଟିଂସରେ ଏକ ଡିଜିଟାଲ୍ ଆସିଷ୍ଟାଣ୍ଟ ଆପ୍ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="assistant_not_selected_text" msgid="3244613673884359276">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା କିମ୍ବା ଅନୁବାଦ କରିବା ପାଇଁ, ସେଟିଂସରେ ଏକ digital assistant ଆପ ବାଛନ୍ତୁ"</string>
     <string name="assistant_not_supported_title" msgid="1675788067597484142">"ଏହି ଫିଚର୍ ବ୍ୟବହାର କରିବା ପାଇଁ ଆପଣଙ୍କ ଆସିଷ୍ଟାଣ୍ଟକୁ ବଦଳାନ୍ତୁ"</string>
-    <string name="assistant_not_supported_text" msgid="1708031078549268884">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା ପାଇଁ କିମ୍ବା ଅନୁବାଦ କରିବାକୁ, ସେଟିଂସରେ ଆପଣଙ୍କ ଡିଜିଟାଲ୍ ଆସିଷ୍ଟାଣ୍ଟ ଆପକୁ ବଦଳାନ୍ତୁ"</string>
+    <string name="assistant_not_supported_text" msgid="1708031078549268884">"ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା କିମ୍ବା ଅନୁବାଦ କରିବା ପାଇଁ, ସେଟିଂସରେ ଆପଣଙ୍କ Digital assistant ଆପକୁ ବଦଳାନ୍ତୁ"</string>
     <string name="tooltip_listen" msgid="7634466447860989102">"ଏହି ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଶୁଣିବା ପାଇଁ ଏଠାରେ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="tooltip_translate" msgid="4184845868901542567">"ଏହି ସ୍କ୍ରିନରେ ଥିବା ଟେକ୍ସଟକୁ ଅନୁବାଦ କରିବା ପାଇଁ ଏଠାରେ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="toast_p2p_app_not_shareable" msgid="7229739094132131536">"ଏହି ଆପ ସେୟାର କରାଯାଇପାରିବ ନାହିଁ"</string>
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index c559988..84892f1 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -156,6 +156,12 @@
   ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30;
   ALL_APPS_SEARCH_RESULT_ASSISTANT_MEMORY = 31;
 
+  // Suggestion Type provided by AGA
+  ONE_SEARCH_WEB_QUERY = 32;
+  ONE_SEARCH_WEB_TRENDING = 33;
+  ONE_SEARCH_WEB_ENTITY = 34;
+  ONE_SEARCH_WEB_ANSWER = 35;
+
   WIDGETS_BOTTOM_TRAY = 28;
   WIDGETS_TRAY_PREDICTION = 29;
 }
diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml
index 11d75c7..7dc0cbe 100644
--- a/quickstep/res/layout/taskbar_all_apps.xml
+++ b/quickstep/res/layout/taskbar_all_apps.xml
@@ -14,12 +14,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.taskbar.TaskbarAllAppsSlideInView
+<com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <com.android.launcher3.taskbar.TaskbarAllAppsContainerView
+    <com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView
         android:id="@+id/apps_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -52,5 +52,5 @@
         </com.android.launcher3.allapps.FloatingHeaderView>
 
         <include layout="@layout/all_apps_fast_scroller" />
-    </com.android.launcher3.taskbar.TaskbarAllAppsContainerView>
-</com.android.launcher3.taskbar.TaskbarAllAppsSlideInView>
+    </com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView>
+</com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 58b26d1..5deea3b 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"رجوع"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"‏مفتاح التبديل إلى IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"الأحدث"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"الإشعارات"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"إعدادات سريعة"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 38a506f..d48c2f1 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"উভতি যাওক"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ছুইচ্চাৰ"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"শেহতীয়া"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"জাননী"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ক্ষিপ্ৰ ছেটিং"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 74fcb63..9cea3c6 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Geriyə"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME keçiricisi"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Sonuncular"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirişlər"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Sürətli Ayarlar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
 </resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 4872f7d..16aec9b 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Назад"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Выключальнік IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Нядаўнія"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Апавяшчэнні"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Хуткія налады"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 3fea5ee..2d3dc60 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Назад"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Редактор за метода на въвежд.: Превключвател"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Скорошни"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Известия"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Бързи настройки"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 0f6c486..8a6d153 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Enrere"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Selector d\'IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recents"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificacions"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. ràpida"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 6aef1a4..c207103 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Zpět"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Přepínač IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Poslední"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Oznámení"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rychlé nastavení"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index c2e6681..e53dfb0 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tilbage"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-vælger"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Seneste"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifikationer"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kvikmenu"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index b5dc6e6..0916fc2 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atrás"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Interruptor IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recientes"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificaciones"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ajustes rápidos"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 950263f..5b4b1ce 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tagasi"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME vahetaja"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Hiljutised"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Märguanded"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kiirseaded"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 9505039..4677eda 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Takaisin"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-vaihtopalvelu"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Viimeaikaiset"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Ilmoitukset"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Pika-asetukset"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index e34cb5d..2a38ff8 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Retour"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Sélecteur IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Récents"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Paramètres rapides"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 3c28c46..c1dd76e 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Retour"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Sélecteur IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Récents"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifications"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Réglages rapides"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 4095f7d..8702947 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atrás"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Selector do IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recentes"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificacións"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Configuración rápida"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 5b1aff9..a84dadd 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Vissza"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-váltó"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Legutóbbiak"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Értesítések"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Gyorsbeállítások"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
 </resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 1327552..f888bea 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Til baka"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Breyta innsláttaraðferð"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nýlegt"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Tilkynningar"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Flýtistillingar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index a555636..344c2a1 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Indietro"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Selettore IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recenti"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notifiche"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Impostazioni rapide"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index cd97903..25d92cb 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"חזרה"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"‏כלי להחלפת IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"לאחרונה"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"התראות"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"הגדרות מהירות"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
 </resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 3295a63..e006c6a 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ថយក្រោយ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"ប៊ូតុងប្ដូរ IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ថ្មីៗ"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ការ​ជូនដំណឹង"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ការកំណត់រហ័ស"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 08e5642..d9ff591 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"뒤로"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME 전환기"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"최근 항목"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"알림"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"빠른 설정"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
 </resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 4c1791f..5c95b03 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ກັບຄືນ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"ຕົວສະຫຼັບ IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ຫຼ້າສຸດ"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ການແຈ້ງເຕືອນ"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ການຕັ້ງຄ່າດ່ວນ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 95a7e86..8b3d2ce 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atgal"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IMRP perjungiklis"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Naujausi"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Pranešimai"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Spartieji nustatymai"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index f3de0b7..99a71fb 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Atpakaļ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME pārslēdzējs"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nesenie"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Paziņojumi"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ātrie iestatīj."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index d09165a..ef6052e 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"နောက်သို့"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ပြောင်းစနစ်"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"လတ်တလောများ"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"အကြောင်းကြားချက်"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"အမြန်ဆက်တင်များ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 6d143bd..a81c779 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tilbake"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-veksler"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nylige"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Varsler"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hurtiginnst."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index cd3383b..3c5d64f 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"पछाडि जानुहोस्"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME स्विचर"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"हालसालैका बटनहरू"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"सूचनाहरू"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"द्रुत सेटिङ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
 </resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 7740783..bb810f0 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -22,7 +22,7 @@
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ପିନ୍‍"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
-    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ୍‍ ବ୍ୟବହାର ସେଟିଂସ୍‍"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ ବ୍ୟବହାର ସେଟିଂସ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍‌"</string>
     <string name="task_view_closed" msgid="9170038230110856166">"ଟାସ୍କ ବନ୍ଦ ହୋଇଯାଇଛି"</string>
@@ -38,7 +38,7 @@
     <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"ଆପଣଙ୍କର ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକୁ, ସିଧା ମୂଳ ସ୍କ୍ରିନରେ ସହଜରେ ଆକ୍ସେସ୍ କରନ୍ତୁ। ଆପଣଙ୍କ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରାମର୍ଶଗୁଡ଼ିକ ପରିବର୍ତ୍ତିତ ହେବ। ତଳ ଧାଡ଼ିରେ ଥିବା ଆପଗୁଡ଼ିକ ଏକ ନୂଆ ଫୋଲ୍ଡରକୁ ମୁଭ୍ କରିଯିବ।"</string>
     <string name="hotseat_edu_accept" msgid="1611544083278999837">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
     <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ନାହିଁ, ଥାଉ"</string>
-    <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ସେଟିଂସ୍"</string>
+    <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ସେଟିଂସ"</string>
     <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯାଏ ଏବଂ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରିବର୍ତ୍ତିତ ହୋଇଥାଏ"</string>
     <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଇବାକୁ ଆପଗୁଡ଼ିକୁ ତଳ ଧାଡ଼ିରୁ ଟାଣି ଆଣନ୍ତୁ"</string>
     <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ଖାଲି ସ୍ଥାନରେ ଯୋଗ କରାଯାଇଛି"</string>
@@ -69,14 +69,14 @@
     <string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ଆପଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ, ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ, ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ୍ କରନ୍ତୁ।"</string>
     <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ସବୁ ପ୍ରସ୍ତୁତ"</string>
     <string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ହୋଇଗଲା"</string>
-    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ସେଟିଂସ୍"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ସେଟିଂସ"</string>
     <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ବଢ଼ିଆ!"</string>
     <string name="gesture_tutorial_step" msgid="1279786122817620968">"ଟ୍ୟୁଟୋରିଆଲ୍ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="allset_title" msgid="5021126669778966707">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବେ ପ୍ରସ୍ତୁତ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ମୂଳପୃଷ୍ଠାକୁ ଯିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
     <string name="allset_description" msgid="6350320429953234580">"ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରିବା ପାଇଁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି"</string>
-    <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ସେଟିଂସ୍"</annotation></string>
+    <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"ସିଷ୍ଟମ ନାଭିଗେସନ ସେଟିଂସ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"ସେୟାର୍ କରନ୍ତୁ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</string>
     <string name="action_split" msgid="2098009717623550676">"ସ୍ପ୍ଲିଟ୍"</string>
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ପଛକୁ ଫେରନ୍ତୁ"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME ସ୍ୱିଚର"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"ବର୍ତ୍ତମାନର"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"କ୍ୱିକ ସେଟିଂସ"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
 </resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 990017e..3c15a85 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"ආපසු"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME මාරුව"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"මෑත"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"දැනුම්දීම්"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ඉක්මන් සැකසීම්"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index a18f72c..155da05 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Späť"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Prepínač IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Nedávne"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Upozornenia"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rýchle nastavenia"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index f2b2fa8..999768c 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Pas"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Çelësi IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Të fundit"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Njoftimet"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cilësimet shpejt"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 2f1b75a..49f20c0 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Tillbaka"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME-väljare"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Senaste"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Aviseringar"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Snabbinställn."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 3ecae96..71b01b6 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Bumalik"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME switcher"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Mga Kamakailan"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Mga Notification"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 19ced47..5e9a84b 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Geri"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME değiştirici"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Son Kullanılanlar"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Bildirimler"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hızlı Ayarlar"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 231d128..2e0d3f7 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Назад"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Перемикач IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Нещодавні"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Сповіщення"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Швидкі налаштув."</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 1072c3a..e3c6698 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"Quay lại"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"Trình chuyển đổi IME"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Gần đây"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"Thông báo"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cài đặt nhanh"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 4587d58..a42476c 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"返回"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"IME 切换器"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"最近用过"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"快捷设置"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 2810771..f2fd544 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"返回"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"輸入法編輯器切換工具"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"最近"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 847cb91..f1fe624 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -102,10 +102,8 @@
     <string name="taskbar_button_back" msgid="8558862226461164514">"返回"</string>
     <string name="taskbar_button_ime_switcher" msgid="1730244360907588541">"輸入法編輯器切換器"</string>
     <string name="taskbar_button_recents" msgid="7273376136216613134">"最近使用"</string>
-    <!-- no translation found for taskbar_button_notifications (7471740351507357318) -->
-    <skip />
-    <!-- no translation found for taskbar_button_quick_settings (227662894293189391) -->
-    <skip />
+    <string name="taskbar_button_notifications" msgid="7471740351507357318">"通知"</string>
+    <string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 6210398..7083180 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -42,6 +42,7 @@
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowInsets;
@@ -66,6 +67,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.OverviewCommandHelper;
@@ -499,6 +501,20 @@
     }
 
     @Override
+    public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+        pendingTasks.add(() -> {
+            // This is added in pending task as we need to wait for views to be positioned
+            // correctly before registering them for the animation.
+            if (mLauncherUnfoldAnimationController != null) {
+                // This is needed in case items are rebound while the unfold animation is in
+                // progress.
+                mLauncherUnfoldAnimationController.updateRegisteredViewsIfNeeded();
+            }
+        });
+        super.onInitialBindComplete(boundPages, pendingTasks);
+    }
+
+    @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
         Stream<SystemShortcut.Factory> base = Stream.of(WellbeingModel.SHORTCUT_FACTORY);
         if (ENABLE_SPLIT_FROM_WORKSPACE.get() && mDeviceProfile.isTablet) {
@@ -535,11 +551,26 @@
     }
 
     /**
-     * Adds a new launch cookie for the activity launch of the given {@param info} if supported.
+     * Adds a new launch cookie for the activity launch if supported.
+     *
+     * @param info the item info for the launch
+     * @param opts the options to set the launchCookie on.
      */
     public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
+        IBinder launchCookie = getLaunchCookie(info);
+        if (launchCookie != null) {
+            opts.setLaunchCookie(launchCookie);
+        }
+    }
+
+    /**
+     * Return a new launch cookie for the activity launch if supported.
+     *
+     * @param info the item info for the launch
+     */
+    public IBinder getLaunchCookie(ItemInfo info) {
         if (info == null) {
-            return;
+            return null;
         }
         switch (info.container) {
             case LauncherSettings.Favorites.CONTAINER_DESKTOP:
@@ -553,8 +584,7 @@
                     break;
                 }
                 // Reset any existing launch cookies associated with the cookie
-                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
-                return;
+                return ObjectWrapper.wrap(NO_MATCHING_ID);
         }
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -565,10 +595,9 @@
                 break;
             default:
                 // Reset any existing launch cookies associated with the cookie
-                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
-                return;
+                return ObjectWrapper.wrap(NO_MATCHING_ID);
         }
-        opts.setLaunchCookie(ObjectWrapper.wrap(new Integer(info.id)));
+        return ObjectWrapper.wrap(new Integer(info.id));
     }
 
     public void setHintUserWillBeActive() {
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 661053a..62603e9 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -16,9 +16,9 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
 
 import android.animation.Animator;
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 3eb1935e..c4a3fb5 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -44,7 +44,7 @@
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index d54b9e7..3b02599 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -23,6 +23,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.app.WallpaperManager;
 import android.os.IBinder;
 import android.os.SystemProperties;
 import android.util.FloatProperty;
@@ -42,7 +43,6 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.systemui.shared.system.BlurUtils;
-import com.android.systemui.shared.system.WallpaperManagerCompat;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -127,7 +127,7 @@
      */
     private int mMaxBlurRadius;
     private boolean mCrossWindowBlursEnabled;
-    private WallpaperManagerCompat mWallpaperManager;
+    private WallpaperManager mWallpaperManager;
     private SurfaceControl mSurface;
     /**
      * How visible the -1 overlay is, from 0 to 1.
@@ -168,7 +168,7 @@
     private void ensureDependencies() {
         if (mWallpaperManager == null) {
             mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
-            mWallpaperManager = new WallpaperManagerCompat(mLauncher);
+            mWallpaperManager = mLauncher.getSystemService(WallpaperManager.class);
         }
 
         if (mLauncher.getRootView() != null && mOnAttachListener == null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
new file mode 100644
index 0000000..27e89ba
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO(b/218912746): Share more behavior to avoid all apps context depending directly on taskbar.
+/** Base for common behavior between taskbar window contexts. */
+public abstract class BaseTaskbarContext extends ContextThemeWrapper implements ActivityContext,
+        DeviceProfileListenable {
+
+    protected final LayoutInflater mLayoutInflater;
+    private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
+
+    protected DeviceProfile mDeviceProfile;
+
+    public BaseTaskbarContext(Context windowContext) {
+        super(windowContext, Themes.getActivityThemeRes(windowContext));
+        mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+    }
+
+    @Override
+    public final LayoutInflater getLayoutInflater() {
+        return mLayoutInflater;
+    }
+
+    @Override
+    public final DeviceProfile getDeviceProfile() {
+        return mDeviceProfile;
+    }
+
+    @Override
+    public final List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
+        return mDPChangeListeners;
+    }
+
+    /** Updates the {@link DeviceProfile} instance to the latest representation of the screen. */
+    public abstract void updateDeviceProfile(DeviceProfile dp);
+
+    /** Callback invoked when a drag is initiated within this context. */
+    public abstract void onDragStart();
+
+    /** Callback invoked when a drag is finished within this context. */
+    public abstract void onDragEnd();
+
+    /** Callback invoked when a popup is shown or closed within this context. */
+    public abstract void onPopupVisibilityChanged(boolean isVisible);
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 0f91aa2..af53ae2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -228,8 +228,7 @@
      * Whether the taskbar education should be shown.
      */
     public boolean shouldShowEdu() {
-        return FeatureFlags.ENABLE_TASKBAR_EDU.get()
-                && !Utilities.IS_RUNNING_IN_TEST_HARNESS
+        return !Utilities.IS_RUNNING_IN_TEST_HARNESS
                 && !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
     }
 
@@ -237,10 +236,6 @@
      * Manually ends the taskbar education flow.
      */
     public void hideEdu() {
-        if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()) {
-            return;
-        }
-
         mControllers.taskbarEduController.hideEdu();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index fe091ef..fa0a606 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -42,10 +43,8 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.Gravity;
-import android.view.LayoutInflater;
 import android.view.RoundedCorner;
 import android.view.View;
 import android.view.WindowManager;
@@ -58,11 +57,8 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -72,11 +68,10 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
 import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.SettingsCache;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
@@ -89,16 +84,13 @@
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
  * that are used by both Launcher and Taskbar (such as Folder) to reference a generic
  * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
  */
-public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext,
-        DeviceProfileListenable {
+public class TaskbarActivityContext extends BaseTaskbarContext {
 
     private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
             SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
@@ -106,13 +98,8 @@
 
     private static final String WINDOW_TITLE = "Taskbar";
 
-    private final LayoutInflater mLayoutInflater;
     private final TaskbarDragLayer mDragLayer;
-    private final TaskbarAllAppsContainerView mAppsView;
     private final TaskbarControllers mControllers;
-    private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
-
-    private DeviceProfile mDeviceProfile;
 
     private final WindowManager mWindowManager;
     private final @Nullable RoundedCorner mLeftCorner, mRightCorner;
@@ -128,6 +115,7 @@
 
     private final boolean mIsSafeModeEnabled;
     private final boolean mIsUserSetupComplete;
+    private final boolean mIsNavBarForceVisible;
     private final boolean mIsNavBarKidsMode;
     private boolean mIsDestroyed = false;
     // The flag to know if the window is excluded from magnification region computation.
@@ -135,14 +123,12 @@
     private boolean mBindingItems = false;
 
     private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
-    private final OnboardingPrefs<TaskbarActivityContext> mOnboardingPrefs;
 
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
             TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
             unfoldTransitionProgressProvider) {
-        super(windowContext, Themes.getActivityThemeRes(windowContext));
+        super(windowContext);
         mDeviceProfile = dp;
-        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
 
         mNavMode = SysUINavigationMode.getMode(windowContext);
         mImeDrawsImeNavBar = SysUINavigationMode.getImeDrawsImeNavBar(windowContext);
@@ -150,6 +136,8 @@
                 () -> getPackageManager().isSafeMode());
         mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
+        mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(
+                Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_FORCE_VISIBLE), 0);
         mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
                 Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
 
@@ -158,8 +146,6 @@
 
         mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size);
 
-        mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
-
         // Inflate views.
         mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
                 R.layout.taskbar, null, false);
@@ -168,11 +154,6 @@
         FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
         StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
 
-        TaskbarAllAppsSlideInView appsSlideInView =
-                (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(R.layout.taskbar_all_apps,
-                        mDragLayer, false);
-        mAppsView = appsSlideInView.getAppsView();
-
         Display display = windowContext.getDisplay();
         Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
                 ? windowContext.getApplicationContext()
@@ -210,7 +191,7 @@
                 new TaskbarAutohideSuspendController(this),
                 new TaskbarPopupController(this),
                 new TaskbarForceVisibleImmersiveController(this),
-                new TaskbarAllAppsViewController(this, appsSlideInView));
+                new TaskbarAllAppsController(this));
     }
 
     public void init(TaskbarSharedState sharedState) {
@@ -236,7 +217,7 @@
         mWindowManager.addView(mDragLayer, mWindowLayoutParams);
     }
 
-    /** Updates the Device profile instance to the latest representation of the screen. */
+    @Override
     public void updateDeviceProfile(DeviceProfile dp) {
         mDeviceProfile = dp;
         updateIconSize(getResources());
@@ -297,31 +278,11 @@
     }
 
     @Override
-    public LayoutInflater getLayoutInflater() {
-        return mLayoutInflater;
-    }
-
-    @Override
     public TaskbarDragLayer getDragLayer() {
         return mDragLayer;
     }
 
     @Override
-    public TaskbarAllAppsContainerView getAppsView() {
-        return mAppsView;
-    }
-
-    @Override
-    public DeviceProfile getDeviceProfile() {
-        return mDeviceProfile;
-    }
-
-    @Override
-    public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
-        return mDPChangeListeners;
-    }
-
-    @Override
     public Rect getFolderBoundingBox() {
         return mControllers.taskbarDragLayerController.getFolderBoundingBox();
     }
@@ -412,11 +373,6 @@
     }
 
     @Override
-    public OnboardingPrefs<TaskbarActivityContext> getOnboardingPrefs() {
-        return mOnboardingPrefs;
-    }
-
-    @Override
     public boolean isBindingItems() {
         return mBindingItems;
     }
@@ -425,6 +381,21 @@
         mBindingItems = bindingItems;
     }
 
+    @Override
+    public void onDragStart() {
+        setTaskbarWindowFullscreen(true);
+    }
+
+    @Override
+    public void onDragEnd() {
+        maybeSetTaskbarWindowNotFullscreen();
+    }
+
+    @Override
+    public void onPopupVisibilityChanged(boolean isVisible) {
+        setTaskbarWindowFocusable(isVisible);
+    }
+
     /**
      * Sets a new data-source for this taskbar instance
      */
@@ -523,6 +494,17 @@
         setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight);
     }
 
+    /**
+     * Reverts Taskbar window to its original size, if all floating views are closed and there is
+     * no system drag operation in progress.
+     */
+    void maybeSetTaskbarWindowNotFullscreen() {
+        if (AbstractFloatingView.getAnyView(this, TYPE_ALL) == null
+                && !mControllers.taskbarDragController.isSystemDragInProgress()) {
+            setTaskbarWindowFullscreen(false);
+        }
+    }
+
     public boolean isTaskbarWindowFullscreen() {
         return mIsFullscreen;
     }
@@ -732,6 +714,10 @@
         return mIsNavBarKidsMode && isThreeButtonNav();
     }
 
+    protected boolean isNavBarForceVisible() {
+        return mIsNavBarForceVisible;
+    }
+
     /**
      * Called when we determine the touchable region.
      *
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
deleted file mode 100644
index 2670200..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
-
-import com.android.launcher3.appprediction.AppsDividerView;
-import com.android.launcher3.appprediction.PredictionRowView;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-
-import java.util.List;
-
-/** Handles the {@link TaskbarAllAppsContainerView} initialization and updates. */
-public final class TaskbarAllAppsViewController {
-
-    private final TaskbarActivityContext mContext;
-    private final TaskbarAllAppsSlideInView mSlideInView;
-    private final TaskbarAllAppsContainerView mAppsView;
-
-    public TaskbarAllAppsViewController(
-            TaskbarActivityContext context, TaskbarAllAppsSlideInView slideInView) {
-        mContext = context;
-        mSlideInView = slideInView;
-        mAppsView = mSlideInView.getAppsView();
-    }
-
-    /** Initialize the controller. */
-    public void init(TaskbarControllers controllers) {
-        if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            return;
-        }
-
-        mAppsView.setOnIconLongClickListener(
-                controllers.taskbarDragController::startDragOnLongClick);
-        mAppsView.getFloatingHeaderView().findFixedRowByType(
-                PredictionRowView.class).setOnIconLongClickListener(
-                controllers.taskbarDragController::startDragOnLongClick);
-    }
-
-    /** Binds the current {@link AppInfo} instances to the {@link TaskbarAllAppsContainerView}. */
-    public void setApps(AppInfo[] apps, int flags) {
-        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            mAppsView.getAppsStore().setApps(apps, flags);
-        }
-    }
-
-    /** Binds the current app predictions to all apps {@link PredictionRowView}. */
-    public void setPredictedApps(List<ItemInfo> predictedApps) {
-        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            PredictionRowView<?> predictionRowView =
-                    mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class);
-            predictionRowView.setPredictedApps(predictedApps);
-        }
-    }
-
-    /** Opens the {@link TaskbarAllAppsContainerView}. */
-    public void show() {
-        if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            return;
-        }
-
-        mAppsView.getFloatingHeaderView().findFixedRowByType(AppsDividerView.class)
-                .setShowAllAppsLabel(
-                        !mContext.getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
-        mContext.getOnboardingPrefs().incrementEventCount(ALL_APPS_VISITED_COUNT);
-        mContext.setTaskbarWindowFullscreen(true);
-        mSlideInView.show();
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 21fbb3b..d2e24e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -20,6 +20,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
 import com.android.systemui.shared.rotation.RotationButtonController;
 
 import java.io.PrintWriter;
@@ -48,7 +49,7 @@
     public final TaskbarAutohideSuspendController taskbarAutohideSuspendController;
     public final TaskbarPopupController taskbarPopupController;
     public final TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController;
-    public final TaskbarAllAppsViewController taskbarAllAppsViewController;
+    public final TaskbarAllAppsController taskbarAllAppsController;
 
     @Nullable private LoggableTaskbarController[] mControllersToLog = null;
 
@@ -74,7 +75,7 @@
             TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
             TaskbarPopupController taskbarPopupController,
             TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
-            TaskbarAllAppsViewController taskbarAllAppsViewController) {
+            TaskbarAllAppsController taskbarAllAppsController) {
         this.taskbarActivityContext = taskbarActivityContext;
         this.taskbarDragController = taskbarDragController;
         this.navButtonController = navButtonController;
@@ -91,7 +92,7 @@
         this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
         this.taskbarPopupController = taskbarPopupController;
         this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
-        this.taskbarAllAppsViewController = taskbarAllAppsViewController;
+        this.taskbarAllAppsController = taskbarAllAppsController;
     }
 
     /**
@@ -115,7 +116,7 @@
         taskbarEduController.init(this);
         taskbarPopupController.init(this);
         taskbarForceVisibleImmersiveController.init(this);
-        taskbarAllAppsViewController.init(this);
+        taskbarAllAppsController.init(this);
 
         mControllersToLog = new LoggableTaskbarController[] {
                 taskbarDragController, navButtonController, navbarButtonsViewController,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 6a2f622..af98b7f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -55,27 +56,25 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
-import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ClipDescriptionCompat;
-import com.android.systemui.shared.system.LauncherAppsCompat;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Collections;
 
 /**
  * Handles long click on Taskbar items to start a system drag and drop operation.
  */
-public class TaskbarDragController extends DragController<TaskbarActivityContext> implements
+public class TaskbarDragController extends DragController<BaseTaskbarContext> implements
         TaskbarControllers.LoggableTaskbarController {
 
     private static boolean DEBUG_DRAG_SHADOW_SURFACE = false;
@@ -95,7 +94,7 @@
     // Animation for the drag shadow back into position after an unsuccessful drag
     private ValueAnimator mReturnAnimator;
 
-    public TaskbarDragController(TaskbarActivityContext activity) {
+    public TaskbarDragController(BaseTaskbarContext activity) {
         super(activity);
         Resources resources = mActivity.getResources();
         mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
@@ -110,7 +109,7 @@
      * generate the ClipDescription and Intent.
      * @return Whether {@link View#startDragAndDrop} started successfully.
      */
-    protected boolean startDragOnLongClick(View view) {
+    public boolean startDragOnLongClick(View view) {
         return startDragOnLongClick(view, null, null);
     }
 
@@ -131,8 +130,7 @@
         }
 
         BubbleTextView btv = (BubbleTextView) view;
-
-        mActivity.setTaskbarWindowFullscreen(true);
+        mActivity.onDragStart();
         btv.post(() -> {
             DragView dragView = startInternalDrag(btv, dragPreviewProvider);
             if (iconShift != null) {
@@ -185,7 +183,7 @@
             }
         };
         if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
-            PopupContainerWithArrow<TaskbarActivityContext> popupContainer =
+            PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
                     mControllers.taskbarPopupController.showForIcon(btv);
             if (popupContainer != null) {
                 dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
@@ -315,13 +313,13 @@
             clipDescription = new ClipDescription(item.title,
                     new String[] {
                             item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
-                                    ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT
-                                    : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY
+                                    ? ClipDescription.MIMETYPE_APPLICATION_SHORTCUT
+                                    : ClipDescription.MIMETYPE_APPLICATION_ACTIVITY
                     });
             intent = new Intent();
             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                 String deepShortcutId = ((WorkspaceItemInfo) item).getDeepShortcutId();
-                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+                intent.putExtra(ClipDescription.EXTRA_PENDING_INTENT,
                         launcherApps.getShortcutIntent(
                                 item.getIntent().getPackage(),
                                 deepShortcutId,
@@ -330,19 +328,19 @@
                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
                 intent.putExtra(Intent.EXTRA_SHORTCUT_ID, deepShortcutId);
             } else {
-                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
-                        LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps,
-                                item.getIntent().getComponent(), null, item.user));
+                intent.putExtra(ClipDescription.EXTRA_PENDING_INTENT,
+                        launcherApps.getMainActivityLaunchIntent(item.getIntent().getComponent(),
+                                null, item.user));
             }
             intent.putExtra(Intent.EXTRA_USER, item.user);
         } else if (tag instanceof Task) {
             Task task = (Task) tag;
             clipDescription = new ClipDescription(task.titleDescription,
                     new String[] {
-                            ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK
+                            ClipDescription.MIMETYPE_APPLICATION_TASK
                     });
             intent = new Intent();
-            intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id);
+            intent.putExtra(Intent.EXTRA_TASK_ID, task.key.id);
             intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId));
         }
 
@@ -394,11 +392,17 @@
         return super.isDragging() || mIsSystemDragInProgress;
     }
 
+    /** {@code true} if the system is currently handling the drag. */
+    public boolean isSystemDragInProgress() {
+        return mIsSystemDragInProgress;
+    }
+
     private void maybeOnDragEnd() {
         if (!isDragging()) {
             ((BubbleTextView) mDragObject.originalView).getIcon().setIsDisabled(false);
             mControllers.taskbarAutohideSuspendController.updateFlag(
                     TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false);
+            mActivity.onDragEnd();
         }
     }
 
@@ -419,23 +423,18 @@
             ItemInfo item = (ItemInfo) tag;
             TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
             if (item.container == CONTAINER_ALL_APPS) {
-                // Since all apps closes when the drag starts, target the all apps button instead
+                // Since all apps closes when the drag starts, target the all apps button instead.
                 target = taskbarViewController.getAllAppsButtonView();
             } else if (item.container >= 0) {
-                // Since folders close when the drag starts, target the folder icon instead
-                LauncherBindableItemsContainer.ItemOperator op = (info, v) -> {
-                    if (info instanceof FolderInfo && v instanceof FolderIcon) {
-                        FolderInfo fi = (FolderInfo) info;
-                        for (WorkspaceItemInfo si : fi.contents) {
-                            if (si.id == item.id) {
-                                // Found the parent
-                                return true;
-                            }
-                        }
-                    }
-                    return false;
-                };
-                target = taskbarViewController.mapOverItems(op);
+                // Since folders close when the drag starts, target the folder icon instead.
+                ItemInfoMatcher matcher = ItemInfoMatcher.forFolderMatch(
+                        ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
+                target = taskbarViewController.getFirstIconMatch(matcher);
+            } else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+                // Find first icon with same package/user as the deep shortcut.
+                ItemInfoMatcher packageUserMatcher = ItemInfoMatcher.ofPackages(
+                        Collections.singleton(item.getTargetPackage()), item.user);
+                target = taskbarViewController.getFirstIconMatch(packageUserMatcher);
             }
         }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 1bd76b9..cdac497 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -181,6 +181,9 @@
             } else if (!mControllers.uiController.isTaskbarTouchable()) {
                 // Let touches pass through us.
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else if (mControllers.taskbarDragController.isSystemDragInProgress()) {
+                // Let touches pass through us.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
             } else if (mControllers.taskbarViewController.areIconsVisible()
                     || AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null
                     || mActivity.isNavBarKidsModeActive()) {
@@ -208,9 +211,7 @@
          * Called when a child is removed from TaskbarDragLayer.
          */
         public void onDragLayerViewRemoved() {
-            if (AbstractFloatingView.getAnyView(mActivity, TYPE_ALL) == null) {
-                mActivity.setTaskbarWindowFullscreen(false);
-            }
+            mActivity.maybeSetTaskbarWindowNotFullscreen();
         }
 
         /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java
index cf28eff..7a42432 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragView.java
@@ -25,8 +25,8 @@
  * while the pre-drag is still in progress (i.e. when the long press popup is still open). After
  * that ends, we switch to a system drag and drop view instead.
  */
-public class TaskbarDragView extends DragView<TaskbarActivityContext> {
-    public TaskbarDragView(TaskbarActivityContext launcher, Drawable drawable, int registrationX,
+public class TaskbarDragView extends DragView<BaseTaskbarContext> {
+    public TaskbarDragView(BaseTaskbarContext launcher, Drawable drawable, int registrationX,
             int registrationY, float initialScale, float scaleOnDrop, float finalScaleDps) {
         super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
                 finalScaleDps);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index 385090f..c99cebb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -71,7 +71,7 @@
     /** Update values tracked via sysui flags. */
     public void updateSysuiFlags(int sysuiFlags) {
         mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_IMMERSIVE_MODE) != 0;
-        if (mContext.isNavBarKidsModeActive()) {
+        if (mContext.isNavBarForceVisible()) {
             if (mIsImmersiveMode) {
                 startIconDimming();
             } else {
@@ -113,7 +113,7 @@
      * Returns whether the taskbar is always visible in immersive mode.
      */
     private boolean isNavbarShownInImmersiveMode() {
-        return mIsImmersiveMode && mContext.isNavBarKidsModeActive();
+        return mIsImmersiveMode && mContext.isNavBarForceVisible();
     }
 
     private void updateIconDimmingAlpha() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 50be430..0f7abda 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -236,8 +236,7 @@
         DeviceProfile dp =
                 mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
 
-        boolean isTaskBarEnabled =
-                FeatureFlags.ENABLE_TASKBAR.get() && dp != null && dp.isTaskbarPresent;
+        boolean isTaskBarEnabled = dp != null && dp.isTaskbarPresent;
 
         if (!isTaskBarEnabled) {
             SystemUiProxy.INSTANCE.get(mContext)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 2e18a40..62392ee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -158,7 +158,7 @@
             mPredictedItems = item.items;
             commitItemsToUI();
         } else if (item.containerId == Favorites.CONTAINER_PREDICTION) {
-            mControllers.taskbarAllAppsViewController.setPredictedApps(item.items);
+            mControllers.taskbarAllAppsController.setPredictedApps(item.items);
         }
     }
 
@@ -201,7 +201,7 @@
 
     @Override
     public void bindAllApplications(AppInfo[] apps, int flags) {
-        mControllers.taskbarAllAppsViewController.setApps(apps, flags);
+        mControllers.taskbarAllAppsController.setApps(apps, flags);
     }
 
     protected void dumpLogs(String prefix, PrintWriter pw) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 68459ab..ea4fe34 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -52,7 +52,7 @@
  */
 public class TaskbarPopupController implements TaskbarControllers.LoggableTaskbarController {
 
-    private static final SystemShortcut.Factory<TaskbarActivityContext>
+    private static final SystemShortcut.Factory<BaseTaskbarContext>
             APP_INFO = SystemShortcut.AppInfo::new;
 
     private final TaskbarActivityContext mContext;
@@ -121,8 +121,8 @@
      * Shows the notifications and deep shortcuts associated with a Taskbar {@param icon}.
      * @return the container if shown or null.
      */
-    public PopupContainerWithArrow<TaskbarActivityContext> showForIcon(BubbleTextView icon) {
-        TaskbarActivityContext context = ActivityContext.lookupContext(icon.getContext());
+    public PopupContainerWithArrow<BaseTaskbarContext> showForIcon(BubbleTextView icon) {
+        BaseTaskbarContext context = ActivityContext.lookupContext(icon.getContext());
         if (PopupContainerWithArrow.getOpen(context) != null) {
             // There is already an items container open, so don't open this one.
             icon.clearFocus();
@@ -133,11 +133,11 @@
             return null;
         }
 
-        final PopupContainerWithArrow<TaskbarActivityContext> container =
-                (PopupContainerWithArrow) context.getLayoutInflater().inflate(
+        final PopupContainerWithArrow<BaseTaskbarContext> container =
+                (PopupContainerWithArrow<BaseTaskbarContext>) context.getLayoutInflater().inflate(
                         R.layout.popup_container, context.getDragLayer(), false);
         container.addOnAttachStateChangeListener(
-                new PopupLiveUpdateHandler<TaskbarActivityContext>(mContext, container) {
+                new PopupLiveUpdateHandler<BaseTaskbarContext>(context, container) {
                     @Override
                     protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
                         showForIcon(originalIcon);
@@ -157,10 +157,9 @@
         container.requestFocus();
 
         // Make focusable to receive back events
-        mControllers.taskbarActivityContext.setTaskbarWindowFocusable(true);
+        context.onPopupVisibilityChanged(true);
         container.setOnCloseCallback(() -> {
-            mControllers.taskbarActivityContext.getDragLayer().post(
-                    () -> mControllers.taskbarActivityContext.setTaskbarWindowFocusable(false));
+            context.getDragLayer().post(() -> context.onPopupVisibilityChanged(false));
             container.setOnCloseCallback(null);
         });
 
@@ -207,7 +206,8 @@
             iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
             iconShift.y = mIconLastTouchPos.y - mContext.getDeviceProfile().iconSizePx;
 
-            mControllers.taskbarDragController.startDragOnLongClick(sv, iconShift);
+            ((TaskbarDragController) ActivityContext.lookupContext(
+                    v.getContext()).getDragController()).startDragOnLongClick(sv, iconShift);
 
             return false;
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index 1589582..f131595 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -35,7 +35,6 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.LauncherAppsCompat;
 
 import java.util.List;
 
@@ -94,8 +93,7 @@
                         info.user);
             } else {
                 SystemUiProxy.INSTANCE.get(mContext).startIntent(
-                        LauncherAppsCompat.getMainActivityLaunchIntent(
-                                mLauncherApps,
+                        mLauncherApps.getMainActivityLaunchIntent(
                                 item.getIntent().getComponent(),
                                 /* startActivityOptions= */null,
                                 item.user),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 335b637..6bc4c0a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -29,10 +29,12 @@
 import android.content.SharedPreferences;
 import android.view.ViewConfiguration;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.StringJoiner;
@@ -51,23 +53,25 @@
     public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 4; // setup wizard and AllSetActivity
     public static final int FLAG_STASHED_IN_APP_IME = 1 << 5; // IME is visible
     public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6;
+    public static final int FLAG_STASHED_IN_APP_ALL_APPS = 1 << 7; // All apps is visible.
 
     // If we're in an app and any of these flags are enabled, taskbar should be stashed.
     private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
             | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP
-            | FLAG_STASHED_IN_APP_IME;
+            | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_APP_ALL_APPS;
 
     // If any of these flags are enabled, inset apps by our stashed height instead of our unstashed
     // height. This way the reported insets are consistent even during transitions out of the app.
-    // Currently any flag that causes us to stash in an app is included, except for IME since that
-    // covers the underlying app anyway and thus the app shouldn't change insets.
+    // Currently any flag that causes us to stash in an app is included, except for IME or All Apps
+    // since those cover the underlying app anyway and thus the app shouldn't change insets.
     private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP
-            & ~FLAG_STASHED_IN_APP_IME;
+            & ~FLAG_STASHED_IN_APP_IME & ~FLAG_STASHED_IN_APP_ALL_APPS;
 
     /**
      * How long to stash/unstash when manually invoked via long press.
      */
-    public static final long TASKBAR_STASH_DURATION = 300;
+    public static final long TASKBAR_STASH_DURATION =
+            WindowManagerWrapper.ANIMATION_DURATION_RESIZE;
 
     /**
      * How long to stash/unstash when keyboard is appearing/disappearing.
@@ -186,7 +190,7 @@
      * Returns whether the taskbar can visually stash into a handle based on the current device
      * state.
      */
-    private boolean supportsVisualStashing() {
+    public boolean supportsVisualStashing() {
         return !mActivity.isThreeButtonNav();
     }
 
@@ -253,7 +257,14 @@
      * Returns the height that taskbar will inset when inside apps.
      */
     public int getContentHeightToReportToApps() {
-        if (hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
+        if (supportsVisualStashing() && hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
+            DeviceProfile dp = mActivity.getDeviceProfile();
+            if (hasAnyFlag(FLAG_STASHED_IN_APP_SETUP) && dp.isTaskbarPresent && !dp.isLandscape) {
+                // We always show the back button in SUW but in portrait the SUW layout may not
+                // be wide enough to support overlapping the nav bar with its content.  For now,
+                // just inset by the bar height.
+                return mUnstashedHeight;
+            }
             boolean isAnimating = mAnimator != null && mAnimator.isStarted();
             return mControllers.stashedHandleViewController.isStashedHandleVisible() || isAnimating
                     ? mStashedHeight : 0;
@@ -585,6 +596,7 @@
         str.add((flags & FLAG_STASHED_IN_APP_IME) != 0 ? "FLAG_STASHED_IN_APP_IME" : "");
         str.add((flags & FLAG_IN_STASHED_LAUNCHER_STATE) != 0
                 ? "FLAG_IN_STASHED_LAUNCHER_STATE" : "");
+        str.add((flags & FLAG_STASHED_IN_APP_ALL_APPS) != 0 ? "FLAG_STASHED_IN_APP_ALL_APPS" : "");
         return str.toString();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index ade58a9..0b537e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.AllAppsButton;
@@ -367,17 +368,36 @@
     }
 
     /**
-     * Maps {@code op} over all the child views, returning the view that {@code op} evaluates
-     * {@code true} for, or {@code null} if none satisfy {@code op}.
+     * Maps {@code op} over all the child views.
      */
-    protected View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
         // map over all the shortcuts on the taskbar
         for (int i = 0; i < getChildCount(); i++) {
             View item = getChildAt(i);
             if (op.evaluate((ItemInfo) item.getTag(), item)) {
-                return item;
+                return;
             }
         }
-        return null;
+    }
+
+    /**
+     * Finds the first icon to match one of the given matchers, from highest to lowest priority.
+     * @return The first match, or All Apps button if no match was found.
+     */
+    public View getFirstMatch(ItemInfoMatcher... matchers) {
+        for (ItemInfoMatcher matcher : matchers) {
+            for (int i = 0; i < getChildCount(); i++) {
+                View item = getChildAt(i);
+                if (!(item.getTag() instanceof ItemInfo)) {
+                    // Should only happen for All Apps button.
+                    continue;
+                }
+                ItemInfo info = (ItemInfo) item.getTag();
+                if (matcher.matchesInfo(info)) {
+                    return item;
+                }
+            }
+        }
+        return mAllAppsButton;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 8b6831b..6e34ee0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
@@ -274,8 +275,22 @@
         mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
     }
 
-    public View mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
-        return mTaskbarView.mapOverItems(op);
+    /**
+     * Maps the given operator to all the top-level children of TaskbarView.
+     */
+    public void mapOverItems(LauncherBindableItemsContainer.ItemOperator op) {
+        mTaskbarView.mapOverItems(op);
+    }
+
+    /**
+     * Returns the first icon to match the given parameter, in priority from:
+     * 1) Icons directly on Taskbar
+     * 2) FolderIcon of the Folder containing the given icon
+     * 3) All Apps button
+     */
+    public View getFirstIconMatch(ItemInfoMatcher matcher) {
+        ItemInfoMatcher folderMatcher = ItemInfoMatcher.forFolderMatch(matcher);
+        return mTaskbarView.getFirstMatch(matcher, folderMatcher);
     }
 
     /**
@@ -306,7 +321,7 @@
         }
 
         public View.OnClickListener getAllAppsButtonClickListener() {
-            return v -> mControllers.taskbarAllAppsViewController.show();
+            return v -> mControllers.taskbarAllAppsController.show();
         }
 
         public View.OnLongClickListener getIconOnLongClickListener() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
similarity index 83%
rename from quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsContainerView.java
rename to quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index d6eb716..b36b9f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.taskbar;
+package com.android.launcher3.taskbar.allapps;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -29,10 +29,7 @@
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
 
 /** All apps container accessible from taskbar. */
-public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView<TaskbarActivityContext> {
-    public TaskbarAllAppsContainerView(Context context) {
-        this(context, null);
-    }
+public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView<TaskbarAllAppsContext> {
 
     public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -44,8 +41,8 @@
 
     @Override
     protected SearchAdapterProvider<?> createMainAdapterProvider() {
-        // Task bar all apps does not yet support search, so this implementation is minimal.
-        return new SearchAdapterProvider<TaskbarActivityContext>(mActivityContext) {
+        // Taskbar all apps does not yet support search, so this implementation is minimal.
+        return new SearchAdapterProvider<TaskbarAllAppsContext>(mActivityContext) {
             @Override
             public boolean launchHighlightedItem() {
                 return false;
@@ -79,8 +76,7 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        // TODO(b/204696617): Switch to status bar insets once they work.
-        setInsets(insets.getInsets(WindowInsets.Type.tappableElement()).toRect());
+        setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
         return super.onApplyWindowInsets(insets);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
new file mode 100644
index 0000000..a67ca70
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.allapps;
+
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowInsets;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.BaseTaskbarContext;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarDragController;
+import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
+
+/**
+ * Window context for the taskbar all apps overlay.
+ * <p>
+ * All apps has its own window and needs a window context. Some properties are delegated to the
+ * {@link TaskbarActivityContext} such as {@link DeviceProfile} and {@link PopupDataProvider}.
+ */
+class TaskbarAllAppsContext extends BaseTaskbarContext {
+    private final TaskbarActivityContext mTaskbarContext;
+    private final OnboardingPrefs<TaskbarAllAppsContext> mOnboardingPrefs;
+
+    private final TaskbarAllAppsController mWindowController;
+    private final TaskbarAllAppsViewController mAllAppsViewController;
+    private final TaskbarDragController mDragController;
+    private final TaskbarAllAppsDragLayer mDragLayer;
+    private final TaskbarAllAppsContainerView mAppsView;
+
+    // We automatically stash taskbar when all apps is opened in gesture navigation mode.
+    private final boolean mWillTaskbarBeVisuallyStashed;
+    private final int mStashedTaskbarHeight;
+
+    TaskbarAllAppsContext(
+            TaskbarActivityContext taskbarContext,
+            TaskbarAllAppsController windowController,
+            TaskbarStashController taskbarStashController) {
+        super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
+        mTaskbarContext = taskbarContext;
+        mDeviceProfile = taskbarContext.getDeviceProfile();
+        mDragController = new TaskbarDragController(this);
+        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+
+        mDragLayer = new TaskbarAllAppsDragLayer(this);
+        TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
+                R.layout.taskbar_all_apps, mDragLayer, false);
+        mWindowController = windowController;
+        mAllAppsViewController = new TaskbarAllAppsViewController(
+                this,
+                slideInView,
+                windowController,
+                taskbarStashController);
+        mAppsView = slideInView.getAppsView();
+
+        mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
+        mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
+    }
+
+    TaskbarAllAppsViewController getAllAppsViewController() {
+        return mAllAppsViewController;
+    }
+
+    @Override
+    public TaskbarDragController getDragController() {
+        return mDragController;
+    }
+
+    @Override
+    public TaskbarAllAppsDragLayer getDragLayer() {
+        return mDragLayer;
+    }
+
+    @Override
+    public TaskbarAllAppsContainerView getAppsView() {
+        return mAppsView;
+    }
+
+    @Override
+    public OnboardingPrefs<TaskbarAllAppsContext> getOnboardingPrefs() {
+        return mOnboardingPrefs;
+    }
+
+    @Override
+    public boolean isBindingItems() {
+        return mTaskbarContext.isBindingItems();
+    }
+
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
+        return mTaskbarContext.getItemOnClickListener();
+    }
+
+    @Override
+    public PopupDataProvider getPopupDataProvider() {
+        return mTaskbarContext.getPopupDataProvider();
+    }
+
+    @Override
+    public DotInfo getDotInfoForItem(ItemInfo info) {
+        return mTaskbarContext.getDotInfoForItem(info);
+    }
+
+    @Override
+    public void updateDeviceProfile(DeviceProfile dp) {
+        mDeviceProfile = dp;
+        dispatchDeviceProfileChanged();
+    }
+
+    @Override
+    public void onDragStart() {}
+
+    @Override
+    public void onDragEnd() {
+        mWindowController.maybeCloseWindow();
+    }
+
+    @Override
+    public void onPopupVisibilityChanged(boolean isVisible) {}
+
+    /** Root drag layer for this context. */
+    private static class TaskbarAllAppsDragLayer extends
+            BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInsetsListener {
+
+        private TaskbarAllAppsDragLayer(Context context) {
+            super(context, null, 1);
+            setClipChildren(false);
+            recreateControllers();
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            ViewTreeObserverWrapper.addOnComputeInsetsListener(
+                    getViewTreeObserver(), this);
+            mActivity.mAllAppsViewController.show();
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            ViewTreeObserverWrapper.removeOnComputeInsetsListener(this);
+        }
+
+        @Override
+        public void recreateControllers() {
+            mControllers = new TouchController[]{mActivity.mDragController};
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
+                AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+                if (topView != null && topView.onBackPressed()) {
+                    return true;
+                }
+            }
+            return super.dispatchKeyEvent(event);
+        }
+
+        @Override
+        public void onComputeInsets(InsetsInfo inoutInfo) {
+            if (mActivity.mDragController.isSystemDragInProgress()) {
+                inoutInfo.touchableRegion.setEmpty();
+                inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            }
+        }
+
+        @Override
+        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+            return updateInsetsDueToStashing(insets);
+        }
+
+        /**
+         * Taskbar automatically stashes when opening all apps, but we don't report the insets as
+         * changing to avoid moving the underlying app. But internally, the apps view should still
+         * layout according to the stashed insets rather than the unstashed insets. So this method
+         * does two things:
+         * 1) Sets navigationBars bottom inset to stashedHeight.
+         * 2) Sets tappableInsets bottom inset to 0.
+         */
+        private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
+            if (!mActivity.mWillTaskbarBeVisuallyStashed) {
+                return oldInsets;
+            }
+            WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+            Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+            Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+                    mActivity.mStashedTaskbarHeight);
+            updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+
+            Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+            Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+                    oldTappableInsets.right, 0);
+            updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+
+            return updatedInsetsBuilder.build();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
new file mode 100644
index 0000000..321ff79
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.allapps;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Handles the all apps overlay window initialization, updates, and its data.
+ * <p>
+ * All apps is in an application overlay window instead of taskbar's navigation bar panel window,
+ * because a navigation bar panel is higher than UI components that all apps should be below such as
+ * the notification tray.
+ * <p>
+ * The all apps window is created and destroyed upon opening and closing all apps, respectively.
+ * Application data may be bound while the window does not exist, so this controller will store
+ * the models for the next all apps session.
+ */
+public final class TaskbarAllAppsController implements OnDeviceProfileChangeListener {
+
+    private static final String WINDOW_TITLE = "Taskbar All Apps";
+
+    private final TaskbarActivityContext mTaskbarContext;
+    private final TaskbarAllAppsProxyView mProxyView;
+    private final LayoutParams mLayoutParams;
+
+    private TaskbarControllers mControllers;
+    /** Window context for all apps if it is open. */
+    private @Nullable TaskbarAllAppsContext mAllAppsContext;
+
+    // Application data models.
+    private AppInfo[] mApps;
+    private int mAppsModelFlags;
+    private List<ItemInfo> mPredictedApps;
+
+    public TaskbarAllAppsController(TaskbarActivityContext context) {
+        mTaskbarContext = context;
+        mProxyView = new TaskbarAllAppsProxyView(mTaskbarContext);
+        mLayoutParams = createLayoutParams();
+    }
+
+    /** Initialize the controller. */
+    public void init(TaskbarControllers controllers) {
+        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            mControllers = controllers;
+        }
+    }
+
+    /** Updates the current {@link AppInfo} instances. */
+    public void setApps(AppInfo[] apps, int flags) {
+        if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            return;
+        }
+
+        mApps = apps;
+        mAppsModelFlags = flags;
+        if (mAllAppsContext != null) {
+            mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
+        }
+    }
+
+    /** Updates the current predictions. */
+    public void setPredictedApps(List<ItemInfo> predictedApps) {
+        if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            return;
+        }
+
+        mPredictedApps = predictedApps;
+        if (mAllAppsContext != null) {
+            mAllAppsContext.getAppsView().getFloatingHeaderView()
+                    .findFixedRowByType(PredictionRowView.class)
+                    .setPredictedApps(mPredictedApps);
+        }
+    }
+
+    /** Opens the {@link TaskbarAllAppsContainerView} in a new window. */
+    public void show() {
+        if (mProxyView.isOpen()) {
+            return;
+        }
+        mProxyView.show();
+
+        mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext,
+                this,
+                mControllers.taskbarStashController);
+        mAllAppsContext.getDragController().init(mControllers);
+        mTaskbarContext.addOnDeviceProfileChangeListener(this);
+        Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
+                .ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
+
+        mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
+        mAllAppsContext.getAppsView().getFloatingHeaderView()
+                .findFixedRowByType(PredictionRowView.class)
+                .setPredictedApps(mPredictedApps);
+    }
+
+    /**
+     * Removes the all apps window from the hierarchy, if all floating views are closed and there is
+     * no system drag operation in progress.
+     * <p>
+     * This method should be called after an exit animation finishes, if applicable.
+     */
+    void maybeCloseWindow() {
+        if (AbstractFloatingView.getOpenView(mAllAppsContext, TYPE_ALL) != null
+                || mAllAppsContext.getDragController().isSystemDragInProgress()) {
+            return;
+        }
+        mProxyView.close(false);
+        mTaskbarContext.removeOnDeviceProfileChangeListener(this);
+        Optional.ofNullable(mAllAppsContext)
+                .map(c -> c.getSystemService(WindowManager.class))
+                .ifPresent(m -> m.removeView(mAllAppsContext.getDragLayer()));
+        mAllAppsContext = null;
+    }
+
+    private LayoutParams createLayoutParams() {
+        LayoutParams layoutParams = new LayoutParams(
+                TYPE_APPLICATION_OVERLAY,
+                0,
+                PixelFormat.TRANSLUCENT);
+        layoutParams.setTitle(WINDOW_TITLE);
+        layoutParams.gravity = Gravity.BOTTOM;
+        layoutParams.packageName = mTaskbarContext.getPackageName();
+        layoutParams.setFitInsetsTypes(0); // Handled by container view.
+        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        layoutParams.setSystemApplicationOverlay(true);
+        return layoutParams;
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        Optional.ofNullable(mAllAppsContext).ifPresent(c -> c.updateDeviceProfile(dp));
+    }
+
+    /**
+     * Proxy view connecting taskbar drag layer to the all apps window.
+     * <p>
+     * The all apps view is in a separate window and has its own drag layer, but this proxy lets it
+     * behave as though its in the taskbar drag layer. For instance, when the taskbar closes all
+     * {@link AbstractFloatingView} instances, the all apps window will also close.
+     */
+    private class TaskbarAllAppsProxyView extends AbstractFloatingView {
+
+        private TaskbarAllAppsProxyView(Context context) {
+            super(context, null);
+        }
+
+        private void show() {
+            mIsOpen = true;
+            mTaskbarContext.getDragLayer().addView(this);
+        }
+
+        @Override
+        protected void handleClose(boolean animate) {
+            mTaskbarContext.getDragLayer().removeView(this);
+            Optional.ofNullable(mAllAppsContext)
+                    .map(TaskbarAllAppsContext::getAllAppsViewController)
+                    .ifPresent(v -> v.close(animate));
+        }
+
+        @Override
+        protected boolean isOfType(int type) {
+            return (type & TYPE_TASKBAR_ALL_APPS) != 0;
+        }
+
+        @Override
+        public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+            return false;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
similarity index 61%
rename from quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsSlideInView.java
rename to quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 63690c4..02aa3f2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.taskbar;
+package com.android.launcher3.taskbar.allapps;
 
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 
@@ -23,18 +23,22 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.views.AbstractSlideInView;
 
-/** Wrapper for taskbar all apps with slide-in behavior. */
-public class TaskbarAllAppsSlideInView extends
-        AbstractSlideInView<TaskbarActivityContext> implements Insettable {
+import java.util.Optional;
 
-    private static final int DEFAULT_OPEN_DURATION = 500;
-    private static final int DEFAULT_CLOSE_DURATION = 200;
+/** Wrapper for taskbar all apps with slide-in behavior. */
+public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllAppsContext>
+        implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
+    static final int DEFAULT_OPEN_DURATION = 500;
+    static final int DEFAULT_CLOSE_DURATION = 200;
 
     private TaskbarAllAppsContainerView mAppsView;
+    private OnCloseListener mOnCloseBeginListener;
+    private float mShiftRange;
 
     public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -46,7 +50,7 @@
     }
 
     /** Opens the all apps view. */
-    public void show() {
+    void show() {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
             return;
         }
@@ -60,12 +64,18 @@
     }
 
     /** The apps container inside this view. */
-    public TaskbarAllAppsContainerView getAppsView() {
+    TaskbarAllAppsContainerView getAppsView() {
         return mAppsView;
     }
 
+    /** Callback invoked when the view is beginning to close (e.g. close animation is started). */
+    void setOnCloseBeginListener(OnCloseListener onCloseBeginListener) {
+        mOnCloseBeginListener = onCloseBeginListener;
+    }
+
     @Override
     protected void handleClose(boolean animate) {
+        Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
         handleClose(animate, DEFAULT_CLOSE_DURATION);
     }
 
@@ -79,6 +89,17 @@
         super.onFinishInflate();
         mAppsView = findViewById(R.id.apps_view);
         mContent = mAppsView;
+
+        DeviceProfile dp = mActivityContext.getDeviceProfile();
+        setShiftRange(dp.allAppsShiftRange);
+
+        mActivityContext.addOnDeviceProfileChangeListener(this);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        setTranslationShift(mTranslationShift);
     }
 
     @Override
@@ -98,4 +119,24 @@
     public void setInsets(Rect insets) {
         mAppsView.setInsets(insets);
     }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        setShiftRange(dp.allAppsShiftRange);
+        setTranslationShift(TRANSLATION_SHIFT_OPENED);
+    }
+
+    private void setShiftRange(float shiftRange) {
+        mShiftRange = shiftRange;
+    }
+
+    @Override
+    protected float getShiftRange() {
+        return mShiftRange;
+    }
+
+    @Override
+    protected boolean isEventOverContent(MotionEvent ev) {
+        return getPopupContainer().isEventOverView(mAppsView.getVisibleContainerView(), ev);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
new file mode 100644
index 0000000..648c486
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.allapps;
+
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_ALL_APPS;
+import static com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView.DEFAULT_CLOSE_DURATION;
+import static com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView.DEFAULT_OPEN_DURATION;
+import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
+
+import com.android.launcher3.appprediction.AppsDividerView;
+import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.taskbar.TaskbarStashController;
+
+/**
+ * Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
+ * taskbar stashing.
+ */
+final class TaskbarAllAppsViewController {
+
+    private final TaskbarAllAppsContext mContext;
+    private final TaskbarAllAppsSlideInView mSlideInView;
+    private final TaskbarAllAppsContainerView mAppsView;
+    private final TaskbarStashController mTaskbarStashController;
+
+    TaskbarAllAppsViewController(
+            TaskbarAllAppsContext context,
+            TaskbarAllAppsSlideInView slideInView,
+            TaskbarAllAppsController windowController,
+            TaskbarStashController taskbarStashController) {
+
+        mContext = context;
+        mSlideInView = slideInView;
+        mAppsView = mSlideInView.getAppsView();
+        mTaskbarStashController = taskbarStashController;
+
+        setUpIconLongClick();
+        setUpAppDivider();
+        setUpTaskbarStashing();
+        mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
+    }
+
+    /** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
+    void show() {
+        mSlideInView.show();
+    }
+
+    /** Closes the {@link TaskbarAllAppsSlideInView}. */
+    void close(boolean animate) {
+        mSlideInView.close(animate);
+    }
+
+    private void setUpIconLongClick() {
+        mAppsView.setOnIconLongClickListener(
+                mContext.getDragController()::startDragOnLongClick);
+        mAppsView.getFloatingHeaderView()
+                .findFixedRowByType(PredictionRowView.class)
+                .setOnIconLongClickListener(
+                        mContext.getDragController()::startDragOnLongClick);
+    }
+
+    private void setUpAppDivider() {
+        mAppsView.getFloatingHeaderView()
+                .findFixedRowByType(AppsDividerView.class)
+                .setShowAllAppsLabel(!mContext.getOnboardingPrefs().hasReachedMaxCount(
+                        ALL_APPS_VISITED_COUNT));
+        mContext.getOnboardingPrefs().incrementEventCount(ALL_APPS_VISITED_COUNT);
+    }
+
+    private void setUpTaskbarStashing() {
+        mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, true);
+        mTaskbarStashController.applyState(DEFAULT_OPEN_DURATION);
+        mSlideInView.setOnCloseBeginListener(() -> {
+            mTaskbarStashController.updateStateForFlag(
+                    FLAG_STASHED_IN_APP_ALL_APPS, false);
+            mTaskbarStashController.applyState(DEFAULT_CLOSE_DURATION);
+        });
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index c058aa7..08d147f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -21,6 +21,7 @@
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Pair;
@@ -58,6 +59,12 @@
         Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
         ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
                 .getActivityLaunchOptions(hostView);
+        Object itemInfo = hostView.getTag();
+        IBinder launchCookie = null;
+        if (itemInfo instanceof ItemInfo) {
+            launchCookie = mLauncher.getLaunchCookie((ItemInfo) itemInfo);
+            activityOptions.options.setLaunchCookie(launchCookie);
+        }
         if (Utilities.ATLEAST_S && !pendingIntent.isActivity()) {
             // In the event this pending intent eventually launches an activity, i.e. a trampoline,
             // use the Quickstep transition animation.
@@ -65,17 +72,14 @@
                 ActivityTaskManager.getService()
                         .registerRemoteAnimationForNextActivityStart(
                                 pendingIntent.getCreatorPackage(),
-                                activityOptions.options.getRemoteAnimationAdapter());
+                                activityOptions.options.getRemoteAnimationAdapter(),
+                                launchCookie);
             } catch (RemoteException e) {
                 // Do nothing.
             }
         }
         activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
-        Object itemInfo = hostView.getTag();
-        if (itemInfo instanceof ItemInfo) {
-            mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
-        }
+        activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
         options = Pair.create(options.first, activityOptions.options);
         if (pendingIntent.isActivity()) {
             logAppLaunch(itemInfo);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 26d8f30..cd14b3f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -38,7 +38,7 @@
 
     @Override
     public int getTransitionDuration(Context context) {
-        return 320;
+        return 150;
     }
 
     @Override
@@ -53,18 +53,20 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
-                .getWorkspaceScaleAndTranslation(launcher);
-        scaleAndTranslation.scale = 1;
+        ScaleAndTranslation scaleAndTranslation =
+                new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
+        if (launcher.getDeviceProfile().isTablet) {
+            scaleAndTranslation.scale = 0.97f;
+        } else {
+            ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
+                    .getWorkspaceScaleAndTranslation(launcher);
+            scaleAndTranslation.translationX = overviewScaleAndTranslation.translationX;
+            scaleAndTranslation.translationY = overviewScaleAndTranslation.translationY;
+        }
         return scaleAndTranslation;
     }
 
     @Override
-    public boolean isTaskbarStashed(Launcher launcher) {
-        return true;
-    }
-
-    @Override
     protected float getDepthUnchecked(Context context) {
         // The scrim fades in at approximately 50% of the swipe gesture.
         // This means that the depth should be greater than 1, in order to fully zoom out.
@@ -86,7 +88,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_CONTENT;
+        return ALL_APPS_CONTENT | HOTSEAT_ICONS;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 75cf5cb..bd7e5de 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -22,7 +22,7 @@
 import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
+import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -172,7 +172,8 @@
         } else if (fromState == HINT_STATE && toState == NORMAL) {
             config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
             if (mHintToNormalDuration == -1) {
-                ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
+                ValueAnimator va = getWorkspaceSpringScaleAnimator(mActivity,
+                        mActivity.getWorkspace(),
                         toState.getWorkspaceScaleAndTranslation(mActivity).scale);
                 mHintToNormalDuration = (int) va.getDuration();
             }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index f6148a7..d8f694e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -40,7 +40,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 099915a..f93917f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -23,8 +23,13 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.LINEAR_TELEPORT;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 
 import android.view.MotionEvent;
 
@@ -126,23 +131,31 @@
 
     private StateAnimationConfig getNormalToAllAppsAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
-                ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
-                ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
+        boolean isTablet = mLauncher.getDeviceProfile().isTablet;
+        builder.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
+                ? INSTANT
+                : Interpolators.clampToProgress(ACCEL,
+                        ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
+                        ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
         builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(ACCEL,
                 ALL_APPS_SCRIM_VISIBLE_THRESHOLD,
                 ALL_APPS_SCRIM_OPAQUE_THRESHOLD));
+        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, isTablet ? LINEAR_TELEPORT : LINEAR);
         return builder;
     }
 
     private StateAnimationConfig getAllAppsToNormalAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
-                1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
-                1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
+        boolean isTablet = mLauncher.getDeviceProfile().isTablet;
+        builder.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
+                ? FINAL_FRAME
+                : Interpolators.clampToProgress(DEACCEL,
+                        1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+                        1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
         builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL,
                 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
                 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, isTablet ? LINEAR_TELEPORT : LINEAR);
         return builder;
     }
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 2cb7100..2570e6c 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -32,10 +32,10 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
 import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -833,12 +833,9 @@
         // Notify when the animation starts
         flushOnRecentsAnimationAndLauncherBound();
 
-        // Start hiding the divider
-        setDividerShown(false /* shown */, false /* immediate */);
-
         // Only add the callback to enable the input consumer after we actually have the controller
         mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
-                mRecentsAnimationController::enableInputConsumer);
+                this::startInterceptingTouchesForGesture);
         mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
 
         mPassedOverviewThreshold = false;
@@ -1457,6 +1454,17 @@
         return swipePipToHomeAnimator;
     }
 
+    private void startInterceptingTouchesForGesture() {
+        if (mRecentsAnimationController == null) {
+            return;
+        }
+
+        mRecentsAnimationController.enableInputConsumer();
+
+        // Start hiding the divider
+        setDividerShown(false /* shown */, true /* immediate */);
+    }
+
     private void computeRecentsScrollIfInvisible() {
         if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
             // Views typically don't compute scroll when invisible as an optimization,
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 678372c..5521020 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -23,11 +23,11 @@
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Log;
+import android.view.ThreadedRenderer;
 
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.MainProcessInitializer;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.ThreadedRendererCompat;
 
 @SuppressWarnings("unused")
 @TargetApi(Build.VERSION_CODES.R)
@@ -60,8 +60,8 @@
         super.init(context);
 
         // Elevate GPU priority for Quickstep and Remote animations.
-        ThreadedRendererCompat.setContextPriority(
-                ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+        ThreadedRenderer.setContextPriority(
+                ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
 
         // Enable binder tracing on system server for calls originating from Launcher
         try {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index f343485..c120b32 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -17,9 +17,13 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 
+import android.os.RemoteException;
+import android.util.Log;
 import android.view.IRecentsAnimationController;
 import android.view.SurfaceControl;
+import android.view.WindowManagerGlobal;
 import android.window.PictureInPictureSurfaceTransaction;
 
 import androidx.annotation.NonNull;
@@ -39,6 +43,7 @@
  */
 public class RecentsAnimationController {
 
+    private static final String TAG = "RecentsAnimationController";
     private final RecentsAnimationControllerCompat mController;
     private final Consumer<RecentsAnimationController> mOnFinishedListener;
     private final boolean mAllowMinimizeSplitScreen;
@@ -74,7 +79,16 @@
         if (mUseLauncherSysBarFlags != useLauncherSysBarFlags) {
             mUseLauncherSysBarFlags = useLauncherSysBarFlags;
             UI_HELPER_EXECUTOR.execute(() -> {
-                mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
+                if (!ENABLE_SHELL_TRANSITIONS) {
+                    mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
+                } else {
+                    try {
+                        WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars(
+                                useLauncherSysBarFlags);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to reach window manager", e);
+                    }
+                }
             });
         }
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 4d79202..ad57683 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -32,7 +32,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
@@ -453,8 +453,8 @@
     /**
      * @return whether the global actions dialog is showing
      */
-    public boolean isGlobalActionsShowing() {
-        return (mSystemUiStateFlags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0;
+    public boolean isSystemUiDialogShowing() {
+        return (mSystemUiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0;
     }
 
     /**
@@ -550,15 +550,13 @@
 
     /**
      * @param ev An ACTION_DOWN motion event
-     * @param task Info for the currently running task
      * @return whether the given motion event can trigger the assistant over the current task.
      */
-    public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) {
+    public boolean canTriggerAssistantAction(MotionEvent ev) {
         return mAssistantAvailable
                 && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
                 && mRotationTouchHelper.touchInAssistantRegion(ev)
-                && !isLockToAppActive()
-                && !isGestureBlockedActivity(task);
+                && !isLockToAppActive();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index d28796c..8b00f45 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -816,8 +816,12 @@
     public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
         if (mRecentTasks != null) {
             try {
-                return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
-                        RECENT_IGNORE_UNAVAILABLE, userId)));
+                final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks,
+                        RECENT_IGNORE_UNAVAILABLE, userId);
+                if (rawTasks == null) {
+                    return new ArrayList<>();
+                }
+                return new ArrayList<>(Arrays.asList(rawTasks));
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call getRecentTasks", e);
             }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 5e298f4..783c868 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -44,7 +44,7 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.HashMap;
 
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
@@ -156,12 +156,26 @@
                 RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
                 BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
                 // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
-                RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appearedTaskTargets)
-                        .filter(remoteAnimationTarget ->
-                                remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
+                final ArrayList<RemoteAnimationTargetCompat> tmpNonHomeApps = new ArrayList<>();
+                final ArrayList<RemoteAnimationTargetCompat> tmpHomeApps = new ArrayList<>();
+                for (RemoteAnimationTargetCompat compat : appearedTaskTargets) {
+                    if (compat.activityType != ACTIVITY_TYPE_HOME) {
+                        tmpNonHomeApps.add(compat);
+                    } else {
+                        tmpHomeApps.add(compat);
+                    }
+                }
+                RemoteAnimationTarget[] nonHomeApps = tmpNonHomeApps.stream()
                         .map(RemoteAnimationTargetCompat::unwrap)
                         .toArray(RemoteAnimationTarget[]::new);
-
+                RemoteAnimationTarget[] homeApps = tmpHomeApps.stream()
+                        .map(RemoteAnimationTargetCompat::unwrap)
+                        .toArray(RemoteAnimationTarget[]::new);
+                if (homeApps.length > 0
+                        && activityInterface.getCreatedActivity() instanceof RecentsActivity) {
+                    ((RecentsActivity) activityInterface.getCreatedActivity()).startHome();
+                    return;
+                }
                 RemoteAnimationTarget[] nonAppTargets =
                         SystemUiProxy.INSTANCE.getNoCreate()
                                 .onGoingToRecentsLegacy(false, nonHomeApps);
@@ -198,8 +212,16 @@
             RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
                     mController != null ? mController.getController() : null,
                     mCtx.getIApplicationThread());
-            final ActivityOptions options = ActivityOptionsCompat.makeRemoteTransition(transition)
-                    .setTransientLaunch();
+            final ActivityOptions options = ActivityOptionsCompat.makeRemoteTransition(transition);
+            // Allowing to pause Home if Home is top activity and Recents is not Home. So when user
+            // start home when recents animation is playing, the home activity can be resumed again
+            // to let the transition controller collect Home activity.
+            ActivityManager.RunningTaskInfo rti = gestureState.getRunningTask();
+            boolean homeIsOnTop = rti != null && rti.topActivity != null
+                    && rti.topActivity.equals(gestureState.getHomeIntent().getComponent());
+            if (!homeIsOnTop) {
+                options.setTransientLaunch();
+            }
             options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
             UI_HELPER_EXECUTOR.execute(() -> mCtx.startActivity(intent, options.toBundle()));
         } else {
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 6c71da9..300f085 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 
+import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -48,7 +49,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.TaskDescriptionCompat;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -154,7 +154,7 @@
 
         // Load icon
         // TODO: Load icon resource (b/143363444)
-        Bitmap icon = TaskDescriptionCompat.getIcon(desc, key.userId);
+        Bitmap icon = getIcon(desc, key.userId);
         if (icon != null) {
             entry.icon = getBitmapInfo(
                     new BitmapDrawable(mContext.getResources(), icon),
@@ -193,6 +193,14 @@
         return entry;
     }
 
+    private Bitmap getIcon(ActivityManager.TaskDescription desc, int userId) {
+        if (desc.getInMemoryIcon() != null) {
+            return desc.getInMemoryIcon();
+        }
+        return ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+                desc.getIconFilename(), userId);
+    }
+
     private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
         PackageManager pm = mContext.getPackageManager();
         String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index ecb30e5..e731b79 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -258,9 +258,8 @@
 
         @Override
         protected ActivityOptions makeLaunchOptions(Activity activity) {
-            final ActivityCompat act = new ActivityCompat(activity);
             final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
-                    act.getDisplayId());
+                    activity.getDisplayId());
             if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
                 return null;
             }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 0c34ead..b224089 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -37,7 +37,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
 import android.annotation.TargetApi;
-import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.Service;
@@ -575,26 +574,14 @@
 
                 ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
                 mUncheckedConsumer = mConsumer;
-            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()) {
+            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+                    && mDeviceState.canTriggerAssistantAction(event)) {
                 mGestureState = createGestureState(mGestureState);
-                ActivityManager.RunningTaskInfo runningTask = mGestureState.getRunningTask();
-                if (mDeviceState.canTriggerAssistantAction(event, runningTask)) {
-                    // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
-                    // should not interrupt it. QuickSwitch assumes that interruption can only
-                    // happen if the next gesture is also quick switch.
-                    mUncheckedConsumer = new AssistantInputConsumer(
-                            this,
-                            mGestureState,
-                            InputConsumer.NO_OP, mInputMonitorCompat,
-                            mDeviceState,
-                            event);
-                } else if (mDeviceState.canTriggerOneHandedAction(event)) {
-                    // Consume gesture event for triggering one handed feature.
-                    mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
-                        InputConsumer.NO_OP, mInputMonitorCompat);
-                } else {
-                    mUncheckedConsumer = InputConsumer.NO_OP;
-                }
+                // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
+                // should not interrupt it. QuickSwitch assumes that interruption can only
+                // happen if the next gesture is also quick switch.
+                mUncheckedConsumer = tryCreateAssistantInputConsumer(
+                        InputConsumer.NO_OP, mGestureState, event);
             } else if (mDeviceState.canTriggerOneHandedAction(event)) {
                 // Consume gesture event for triggering one handed feature.
                 mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
@@ -641,6 +628,14 @@
         ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
     }
 
+    private InputConsumer tryCreateAssistantInputConsumer(InputConsumer base,
+            GestureState gestureState, MotionEvent motionEvent) {
+        return mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())
+                ? base
+                : new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat,
+                        mDeviceState, motionEvent);
+    }
+
     public GestureState createGestureState(GestureState previousGestureState) {
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
@@ -686,9 +681,8 @@
             handleOrientationSetup(base);
         }
         if (mDeviceState.isFullyGesturalNavMode()) {
-            if (mDeviceState.canTriggerAssistantAction(event, newGestureState.getRunningTask())) {
-                base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat,
-                        mDeviceState, event);
+            if (mDeviceState.canTriggerAssistantAction(event)) {
+                base = tryCreateAssistantInputConsumer(base, newGestureState, event);
             }
 
             // If Taskbar is present, we listen for long press to unstash it.
@@ -701,7 +695,7 @@
 
             // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
             // instead of going all the way home when a swipe up is detected.
-            if (mDeviceState.isBubblesExpanded() || mDeviceState.isGlobalActionsShowing()) {
+            if (mDeviceState.isBubblesExpanded() || mDeviceState.isSystemUiDialogShowing()) {
                 base = new SysUiOverlayInputConsumer(
                         getBaseContext(), mDeviceState, mInputMonitorCompat);
             }
@@ -768,7 +762,10 @@
         } else if (gestureState.getRunningTask() == null) {
             return getDefaultInputConsumer();
         } else if (previousGestureState.isRunningAnimationToLauncher()
-                || gestureState.getActivityInterface().isResumed()
+                || (gestureState.getActivityInterface().isResumed()
+                        // with shell-transitions, home is resumed during recents animation, so
+                        // explicitly check against recents animation too.
+                        && !previousGestureState.isRecentsAnimationRunning())
                 || forceOverviewInputConsumer) {
             return createOverviewInputConsumer(
                     previousGestureState, gestureState, event, forceOverviewInputConsumer);
@@ -965,6 +962,9 @@
             pw.println("ProtoTrace:");
             pw.println("  file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
             mTaskbarManager.dumpLogs("", pw);
+            if (createdOverviewActivity != null) {
+                createdOverviewActivity.getDeviceProfile().dump("", pw);
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index e290be8..ab26d15 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -21,10 +21,8 @@
 import android.view.ViewRootImpl;
 
 import com.android.launcher3.Utilities;
-import com.android.systemui.shared.system.ViewRootImplCompat;
 
 import java.util.function.BooleanSupplier;
-import java.util.function.LongConsumer;
 
 /**
  * Utility class for helpful methods related to {@link View} objects.
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
index 3f833c0..878f132 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
@@ -15,9 +15,11 @@
  */
 package com.android.quickstep.inputconsumers;
 
+import android.app.ActivityManager;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.PointF;
+import android.os.RemoteException;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.testing.TestLogging;
@@ -36,6 +38,10 @@
  */
 public class SysUiOverlayInputConsumer implements InputConsumer,
         TriggerSwipeUpTouchTracker.OnSwipeUpListener {
+    private static final String TAG = "SysUiOverlayInputConsumer";
+
+    // Should match the values in PhoneWindowManager
+    private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
 
     private final Context mContext;
     private final InputMonitorCompat mInputMonitor;
@@ -76,7 +82,11 @@
     @Override
     public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
         // Close system dialogs when a swipe up is detected.
-        mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+        try {
+            ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_GESTURE_NAV);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception calling closeSystemDialogs " + e.getMessage());
+        }
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 672687d..9a101b9 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -16,7 +16,7 @@
 package com.android.quickstep.interaction;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 12a638a..1b6f39e 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -110,6 +110,11 @@
         return new StatsCompatLogger(mContext, mActivityContext);
     }
 
+    @Override
+    protected StatsLatencyLogger createLatencyLogger() {
+        return new StatsCompatLatencyLogger(mContext, mActivityContext);
+    }
+
     /**
      * Synchronously writes an itemInfo to stats log
      */
@@ -141,7 +146,9 @@
                 getCardinality(info) /* cardinality */,
                 info.getWidget().getSpanX(),
                 info.getWidget().getSpanY(),
-                getFeatures(info));
+                getFeatures(info),
+                null /* attributes */
+        );
     }
 
     /**
@@ -170,7 +177,8 @@
                 info.getAttribute().getNumber(), // attribute_id = 15;
                 getCardinality(info), // cardinality = 16;
                 info.getWidget().getSpanX(), // span_x = 17 [default = 1];
-                info.getWidget().getSpanY() // span_y = 18 [default = 1];
+                info.getWidget().getSpanY(), // span_y = 18 [default = 1];
+                null /* attributes */
         );
     }
 
@@ -413,7 +421,63 @@
                     atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
                     getCardinality(atomInfo) /* cardinality */,
                     getFeatures(atomInfo) /* features */,
-                    getSearchAttributes(atomInfo) /* searchAttributes */
+                    getSearchAttributes(atomInfo) /* searchAttributes */,
+                    null /* attributes */
+            );
+        }
+    }
+
+    /**
+     * Helps to construct and log statsd compatible latency events.
+     */
+    private static class StatsCompatLatencyLogger implements StatsLatencyLogger {
+        private final Context mContext;
+        private final Optional<ActivityContext> mActivityContext;
+        private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
+        private LatencyType mType = LatencyType.UNKNOWN;
+        private long mLatencyInMillis;
+
+        StatsCompatLatencyLogger(Context context, ActivityContext activityContext) {
+            mContext = context;
+            mActivityContext = Optional.ofNullable(activityContext);
+        }
+
+        @Override
+        public StatsLatencyLogger withInstanceId(InstanceId instanceId) {
+            this.mInstanceId = instanceId;
+            return this;
+        }
+
+        @Override
+        public StatsLatencyLogger withType(LatencyType type) {
+            this.mType = type;
+            return this;
+        }
+
+        @Override
+        public StatsLatencyLogger withLatency(long latencyInMillis) {
+            this.mLatencyInMillis = latencyInMillis;
+            return this;
+        }
+
+        @Override
+        public void log(EventEnum event) {
+            if (IS_VERBOSE) {
+                String name = (event instanceof Enum) ? ((Enum) event).name() :
+                        event.getId() + "";
+
+                Log.d(TAG, mInstanceId == DEFAULT_INSTANCE_ID
+                        ? String.format("\n%s = %dms\n", name, mLatencyInMillis)
+                        : String.format("\n%s = %dms (InstanceId:%s)\n", name,
+                                mLatencyInMillis, mInstanceId));
+            }
+
+            SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_LATENCY,
+                    event.getId(), // event_id
+                    mInstanceId.getId(), // instance_id
+                    0, // package_id
+                    mLatencyInMillis, // latency_in_millis
+                    mType.getId() //type
             );
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 861ff96..143042f 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -36,6 +36,8 @@
     private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
     private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
 
+    private boolean mAnimationInProgress = false;
+
     public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
         mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
                 new LauncherViewsMoveFromCenterTranslationApplier());
@@ -44,6 +46,7 @@
     @CallSuper
     @Override
     public void onTransitionStarted() {
+        mAnimationInProgress = true;
         mMoveFromCenterAnimation.updateDisplayProperties();
         onPrepareViewsForAnimation();
         onTransitionProgress(0f);
@@ -58,7 +61,23 @@
     @CallSuper
     @Override
     public void onTransitionFinished() {
+        mAnimationInProgress = false;
         mMoveFromCenterAnimation.onTransitionFinished();
+        clearRegisteredViews();
+    }
+
+    /**
+     * Re-prepares views for animation. This is useful in case views are re-bound while the
+     * animation is in progress.
+     */
+    public void updateRegisteredViewsIfNeeded() {
+        if (mAnimationInProgress) {
+            clearRegisteredViews();
+            onPrepareViewsForAnimation();
+        }
+    }
+
+    private void clearRegisteredViews() {
         mMoveFromCenterAnimation.clearRegisteredViews();
 
         mOriginalClipChildren.clear();
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 333df10..6f171f9 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -15,14 +15,14 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_UNFOLD_ANIMATION;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY_FACTORY;
+import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.Utilities.comp;
 
 import android.annotation.Nullable;
 import android.util.FloatProperty;
 import android.util.MathUtils;
-import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -30,6 +30,7 @@
 
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.util.HorizontalInsettableView;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
@@ -44,17 +45,20 @@
     // Percentage of the width of the quick search bar that will be reduced
     // from the both sides of the bar when progress is 0
     private static final float MAX_WIDTH_INSET_FRACTION = 0.15f;
-    private static final FloatProperty<View> UNFOLD_SCALE_PROPERTY =
-            SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_UNFOLD_ANIMATION);
+    private static final FloatProperty<Workspace> WORKSPACE_SCALE_PROPERTY =
+            WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_UNFOLD_ANIMATION);
+    private static final FloatProperty<Hotseat> HOTSEAT_SCALE_PROPERTY =
+            HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_UNFOLD_ANIMATION);
 
     private final Launcher mLauncher;
+    private final ScopedUnfoldTransitionProgressProvider mProgressProvider;
+    private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider;
+    private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator;
+    private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator;
 
     @Nullable
     private HorizontalInsettableView mQsbInsettable;
 
-    private final ScopedUnfoldTransitionProgressProvider mProgressProvider;
-    private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider;
-
     public LauncherUnfoldAnimationController(
             Launcher launcher,
             WindowManager windowManager,
@@ -62,21 +66,21 @@
         mLauncher = launcher;
         mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
                 unfoldTransitionProgressProvider);
+        mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
+                windowManager);
+        mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
+                windowManager);
         mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
                 WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
         mNaturalOrientationProgressProvider.init();
 
         // Animated in all orientations
-        mProgressProvider.addCallback(new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
-                windowManager));
-        mProgressProvider
-                .addCallback(new LauncherScaleAnimationListener());
+        mProgressProvider.addCallback(mUnfoldMoveFromCenterWorkspaceAnimator);
+        mProgressProvider.addCallback(new LauncherScaleAnimationListener());
 
         // Animated only in natural orientation
-        mNaturalOrientationProgressProvider
-                .addCallback(new QsbAnimationListener());
-        mNaturalOrientationProgressProvider
-                .addCallback(new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager));
+        mNaturalOrientationProgressProvider.addCallback(new QsbAnimationListener());
+        mNaturalOrientationProgressProvider.addCallback(mUnfoldMoveFromCenterHotseatAnimator);
     }
 
     /**
@@ -108,6 +112,12 @@
         mNaturalOrientationProgressProvider.destroy();
     }
 
+    /** Called when launcher finished binding its items. */
+    public void updateRegisteredViewsIfNeeded() {
+        mUnfoldMoveFromCenterHotseatAnimator.updateRegisteredViewsIfNeeded();
+        mUnfoldMoveFromCenterWorkspaceAnimator.updateRegisteredViewsIfNeeded();
+    }
+
     private class QsbAnimationListener implements TransitionProgressListener {
 
         @Override
@@ -134,6 +144,7 @@
 
         @Override
         public void onTransitionStarted() {
+            mLauncher.getWorkspace().setPivotToScaleWithSelf(mLauncher.getHotseat());
         }
 
         @Override
@@ -147,8 +158,8 @@
         }
 
         private void setScale(float value) {
-            UNFOLD_SCALE_PROPERTY.setValue(mLauncher.getWorkspace(), value);
-            UNFOLD_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value);
+            WORKSPACE_SCALE_PROPERTY.setValue(mLauncher.getWorkspace(), value);
+            HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value);
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
index 8659b68..5326d2b 100644
--- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -15,8 +15,9 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_REVEAL_ANIM;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY_FACTORY;
+import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
@@ -32,6 +33,7 @@
 import android.view.View;
 
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
@@ -51,8 +53,11 @@
 
     // Should be used for animations running alongside this WorkspaceRevealAnim.
     public static final int DURATION_MS = 350;
-    private static final FloatProperty<View> REVEAL_SCALE_PROPERTY =
-            SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_REVEAL_ANIM);
+    private static final FloatProperty<Workspace> WORKSPACE_SCALE_PROPERTY =
+            WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_REVEAL_ANIM);
+
+    private static final FloatProperty<Hotseat> HOTSEAT_SCALE_PROPERTY =
+            HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_REVEAL_ANIM);
 
     private final float mScaleStart;
     private final AnimatorSet mAnimators = new AnimatorSet();
@@ -67,8 +72,8 @@
         workspace.setPivotToScaleWithSelf(launcher.getHotseat());
 
         // Add reveal animations.
-        addRevealAnimatorsForView(workspace);
-        addRevealAnimatorsForView(launcher.getHotseat());
+        addRevealAnimatorsForView(workspace, WORKSPACE_SCALE_PROPERTY);
+        addRevealAnimatorsForView(launcher.getHotseat(), HOTSEAT_SCALE_PROPERTY);
 
         // Add overview scrim animation.
         if (animateOverviewScrim) {
@@ -93,8 +98,8 @@
         mAnimators.setInterpolator(Interpolators.DECELERATED_EASE);
     }
 
-    private void addRevealAnimatorsForView(View v) {
-        ObjectAnimator scale = ObjectAnimator.ofFloat(v, REVEAL_SCALE_PROPERTY, mScaleStart, 1f);
+    private <T extends View>  void addRevealAnimatorsForView(T v, FloatProperty<T> scaleProperty) {
+        ObjectAnimator scale = ObjectAnimator.ofFloat(v, scaleProperty, mScaleStart, 1f);
         scale.setDuration(DURATION_MS);
         scale.setInterpolator(Interpolators.DECELERATED_EASE);
         mAnimators.play(scale);
@@ -107,7 +112,7 @@
         mAnimators.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                REVEAL_SCALE_PROPERTY.set(v, 1f);
+                scaleProperty.set(v, 1f);
                 v.setAlpha(1f);
             }
         });
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java
new file mode 100644
index 0000000..cfb0056
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskThumbnailView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A child view of {@link com.android.quickstep.views.FloatingTaskView} to draw the thumbnail in a
+ * rounded corner frame. While the purpose of this class sounds similar to
+ * {@link TaskThumbnailView}, it doesn't need a lot of complex logic in {@link TaskThumbnailView}
+ * in relation to moving with {@link RecentsView}.
+ */
+public class FloatingTaskThumbnailView extends View {
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Matrix mMatrix = new Matrix();
+
+    private @Nullable BitmapShader mBitmapShader;
+    private @Nullable Bitmap mBitmap;
+
+    private FloatingTaskView.FullscreenDrawParams mFullscreenParams;
+
+    public FloatingTaskThumbnailView(Context context) {
+        this(context, null);
+    }
+
+    public FloatingTaskThumbnailView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingTaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mFullscreenParams == null || mBitmap == null) {
+            return;
+        }
+
+        // Scale down the bitmap to fix x, and crop in y.
+        float scale = 1.0f * getMeasuredWidth() / mBitmap.getWidth();
+        mMatrix.postScale(scale, scale);
+        mBitmapShader.setLocalMatrix(mMatrix);
+
+        canvas.drawRoundRect(0, 0, getMeasuredWidth(),  getMeasuredHeight(),
+                mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX,
+                mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY, mPaint);
+    }
+
+    public void setThumbnail(Bitmap bitmap) {
+        mBitmap = bitmap;
+        if (bitmap != null) {
+            mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mPaint.setShader(mBitmapShader);
+        }
+    }
+
+    public void setFullscreenParams(FloatingTaskView.FullscreenDrawParams fullscreenParams) {
+        mFullscreenParams = fullscreenParams;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index f2f1c3f..c59dc18 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -15,7 +15,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
 
@@ -29,6 +28,8 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.TaskCornerRadius;
+import com.android.systemui.shared.system.QuickStepContract;
 
 import java.util.function.Consumer;
 
@@ -50,9 +51,9 @@
     private RectF mStartingPosition;
     private final StatefulActivity mActivity;
     private final boolean mIsRtl;
-    private final Rect mOutline = new Rect();
+    private final FullscreenDrawParams mCurrentFullscreenParams;
     private PagedOrientationHandler mOrientationHandler;
-    private ImageView mImageView;
+    private FloatingTaskThumbnailView mThumbnailView;
 
     public FloatingTaskView(Context context) {
         this(context, null);
@@ -66,16 +67,17 @@
         super(context, attrs, defStyleAttr);
         mActivity = BaseActivity.fromContext(context);
         mIsRtl = Utilities.isRtl(getResources());
+        mCurrentFullscreenParams = new FullscreenDrawParams(context);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mImageView = findViewById(R.id.thumbnail);
-        mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-        mImageView.setLayerType(LAYER_TYPE_HARDWARE, null);
+        mThumbnailView = findViewById(R.id.thumbnail);
+        mThumbnailView.setFullscreenParams(mCurrentFullscreenParams);
         mSplitPlaceholderView = findViewById(R.id.split_placeholder);
         mSplitPlaceholderView.setAlpha(0);
+        mSplitPlaceholderView.setFullscreenParams(mCurrentFullscreenParams);
     }
 
     private void init(StatefulActivity launcher, View originalView, @Nullable Bitmap thumbnail,
@@ -86,13 +88,11 @@
                 (InsettableFrameLayout.LayoutParams) getLayoutParams();
 
         mSplitPlaceholderView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
-        positionOut.round(mOutline);
         setPivotX(0);
         setPivotY(0);
 
         // Copy bounds of exiting thumbnail into ImageView
-        mImageView.setImageBitmap(thumbnail);
-        mImageView.setVisibility(VISIBLE);
+        mThumbnailView.setThumbnail(thumbnail);
 
         RecentsView recentsView = launcher.getOverviewPanel();
         mOrientationHandler = recentsView.getPagedOrientationHandler();
@@ -133,27 +133,24 @@
         setLayoutParams(lp);
     }
 
-    // TODO(194414938) set correct corner radii
-    public void update(RectF position, float progress, float windowRadius) {
+    public void update(RectF position, float progress) {
         MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
 
         float dX = position.left - mStartingPosition.left;
         float dY = position.top - lp.topMargin;
+        float scaleX = position.width() / lp.width;
+        float scaleY = position.height() / lp.height;
+
+        mCurrentFullscreenParams.updateParams(position, progress, scaleX, scaleY);
 
         setTranslationX(dX);
         setTranslationY(dY);
-
-        float scaleX = position.width() / lp.width;
-        float scaleY = position.height() / lp.height;
         setScaleX(scaleX);
         setScaleY(scaleY);
+        mSplitPlaceholderView.invalidate();
+
         float childScaleX = 1f / scaleX;
         float childScaleY = 1f / scaleY;
-
-        invalidate();
-        // TODO(194414938) seems like this scale value could be fine tuned, some stretchiness
-        mImageView.setScaleX(1f / scaleX + scaleX * progress);
-        mImageView.setScaleY(1f / scaleY + scaleY * progress);
         mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIconView(), childScaleX);
         mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIconView(), childScaleY);
     }
@@ -181,7 +178,8 @@
     }
 
     public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
-            boolean fadeWithThumbnail) {
+            boolean fadeWithThumbnail, boolean isInitialSplit) {
+        mCurrentFullscreenParams.setIsInitialSplit(isInitialSplit);
         final BaseDragLayer dragLayer = mActivity.getDragLayer();
         int[] dragLayerBounds = new int[2];
         dragLayer.getLocationOnScreen(dragLayerBounds);
@@ -191,22 +189,16 @@
         ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1);
         animation.add(transitionAnimator);
         long animDuration = animation.getDuration();
-        Rect crop = new Rect();
         RectF floatingTaskViewBounds = new RectF();
-        final float initialWindowRadius = supportsRoundedCornersOnWindows(getResources())
-                ? Math.max(crop.width(), crop.height()) / 2f
-                : 0f;
 
         if (fadeWithThumbnail) {
             animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
                     0, 1, ACCEL);
-            animation.addFloat(mImageView, LauncherAnimUtils.VIEW_ALPHA,
+            animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
                     1, 0, DEACCEL_3);
         }
 
         MultiValueUpdateListener listener = new MultiValueUpdateListener() {
-            final FloatProp mWindowRadius = new FloatProp(initialWindowRadius,
-                    initialWindowRadius, 0, animDuration, LINEAR);
             final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
             final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
             final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
@@ -221,7 +213,7 @@
                 Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value,
                         mTaskViewScaleY.value);
 
-                update(floatingTaskViewBounds, percent, mWindowRadius.value * 1);
+                update(floatingTaskViewBounds, percent);
             }
         };
         transitionAnimator.addUpdateListener(listener);
@@ -250,4 +242,36 @@
             dY = centerY - startTaskViewBounds.centerY();
         }
     }
+
+    public static class FullscreenDrawParams {
+
+        private final float mCornerRadius;
+        private final float mWindowCornerRadius;
+
+        public boolean mIsInitialSplit = true;
+        public final RectF mFloatingTaskViewBounds = new RectF();
+        public float mCurrentDrawnCornerRadius;
+        public float mScaleX = 1;
+        public float mScaleY = 1;
+
+        public FullscreenDrawParams(Context context) {
+            mCornerRadius = TaskCornerRadius.get(context);
+            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
+
+            mCurrentDrawnCornerRadius = mCornerRadius;
+        }
+
+        public void updateParams(RectF floatingTaskViewBounds, float progress, float scaleX,
+                float scaleY) {
+            mFloatingTaskViewBounds.set(floatingTaskViewBounds);
+            mScaleX = scaleX;
+            mScaleY = scaleY;
+            mCurrentDrawnCornerRadius = mIsInitialSplit ? 0 :
+                    Utilities.mapRange(progress, mCornerRadius, mWindowCornerRadius);
+        }
+
+        public void setIsInitialSplit(boolean isInitialSplit) {
+            mIsInitialSplit = isInitialSplit;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index cb7e08a..5e331e2 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2732,7 +2732,7 @@
                     mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
             mFirstFloatingTaskView.setAlpha(1);
             mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
-                    mTempRect, true /*fadeWithThumbnail*/);
+                    mTempRect, true /* fadeWithThumbnail */, true /* isInitialSplit */);
         } else {
             mSplitSelectSource.view.setVisibility(INVISIBLE);
             mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
@@ -2740,7 +2740,7 @@
                     mSplitSelectSource.drawable, startingTaskRect);
             mFirstFloatingTaskView.setAlpha(1);
             mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
-                    mTempRect, true /*fadeWithThumbnail*/);
+                    mTempRect, true /* fadeWithThumbnail */, true /* isInitialSplit */);
         }
         anim.addEndListener(success -> {
             if (success) {
@@ -4030,14 +4030,14 @@
         mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
         mFirstFloatingTaskView.addAnimation(pendingAnimation,
                 new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
-                false /*fadeWithThumbnail*/);
+                false /* fadeWithThumbnail */, false /* isInitialSplit */);
 
         mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
                 thumbnailView, thumbnailView.getThumbnail(),
                 iconView.getDrawable(), secondTaskStartingBounds);
         mSecondFloatingTaskView.setAlpha(1);
         mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
-                secondTaskEndingBounds, true /* fadeWithThumbnail */);
+                secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isInitialSplit */);
         pendingAnimation.addEndListener(aBoolean ->
                 mSplitSelectStateController.setSecondTaskId(task.key.id,
                 aBoolean1 -> RecentsView.this.resetFromSplitSelectionState()));
@@ -4110,7 +4110,7 @@
         mTempRectF.set(mTempRect);
         // TODO(194414938) set correct corner radius
         mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
-        mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f, /*windowRadius=*/0f);
+        mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
 
         PagedOrientationHandler orientationHandler = getPagedOrientationHandler();
         Pair<FloatProperty, FloatProperty> taskViewsFloat =
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index cfa482f..d37dfbf 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -17,9 +17,12 @@
 package com.android.quickstep.views;
 
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.widget.FrameLayout;
 
@@ -27,6 +30,10 @@
 
 public class SplitPlaceholderView extends FrameLayout {
 
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    private FloatingTaskView.FullscreenDrawParams mFullscreenParams;
+
     public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
             new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
                 @Override
@@ -46,6 +53,17 @@
 
     public SplitPlaceholderView(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        mPaint.setColor(getThemePrimaryColor(context));
+        setWillNotDraw(false);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        // Call this before super call to draw below the children.
+        drawBackground(canvas);
+
+        super.dispatchDraw(canvas);
     }
 
     @Nullable
@@ -53,6 +71,10 @@
         return mIconView;
     }
 
+    public void setFullscreenParams(FloatingTaskView.FullscreenDrawParams fullscreenParams) {
+        mFullscreenParams = fullscreenParams;
+    }
+
     public void setIcon(Drawable drawable, int iconSize) {
         if (mIconView == null) {
             mIconView = new IconView(getContext());
@@ -64,4 +86,20 @@
         params.gravity = Gravity.CENTER;
         mIconView.setLayoutParams(params);
     }
+
+    private void drawBackground(Canvas canvas) {
+        if (mFullscreenParams == null) {
+            return;
+        }
+
+        canvas.drawRoundRect(0, 0, getMeasuredWidth(),  getMeasuredHeight(),
+                mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleX,
+                mFullscreenParams.mCurrentDrawnCornerRadius / mFullscreenParams.mScaleY, mPaint);
+    }
+
+    private static int getThemePrimaryColor(Context context) {
+        final TypedValue value = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.colorPrimary, value, true);
+        return value.data;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 60b0d17..11f9ddd 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SystemUiController;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
@@ -161,6 +162,23 @@
         setThumbnail(task, thumbnailData, true /* refreshNow */);
     }
 
+    /**
+     * By combining the two in a single bitmap then we only have to do a single draw
+     * call in the onDraw function. Also, this fixes a bug where the background was
+     * visible in the corners because of anti-aliasing.
+     */
+    public Bitmap combineThumbnailAndBackground(Bitmap bm) {
+        return BitmapRenderer.createHardwareBitmap(bm.getWidth(), bm.getHeight(), c -> {
+            final boolean drawBackgroundOnly = mTask == null || mTask.isLocked;
+            if (drawBackgroundOnly) {
+                c.drawPaint(mBackgroundPaint);
+            } else {
+                c.drawPaint(mBackgroundPaint);
+                c.drawBitmap(bm, 0, 0, null);
+            }
+        });
+    }
+
     /** Updates the shader, paint, matrix to redraw. */
     public void refresh() {
         refresh(false);
@@ -173,6 +191,7 @@
     private void refresh(boolean shouldRefreshOverlay) {
         if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
             Bitmap bm = mThumbnailData.thumbnail;
+            bm = combineThumbnailAndBackground(bm);
             bm.prepareToDraw();
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
             mPaint.setShader(mBitmapShader);
@@ -194,7 +213,6 @@
      * <p>
      * If dimAlpha is 0, no dimming is applied; if dimAlpha is 1, the thumbnail will be the
      * extracted background color.
-     *
      */
     public void setDimAlpha(float dimAlpha) {
         mDimAlpha = dimAlpha;
@@ -296,18 +314,6 @@
                 return;
             }
         }
-
-        // Always draw the background since the snapshots might be translucent or partially empty
-        // (For example, tasks been reparented out of dismissing split root when drag-to-dismiss
-        // split screen).
-        canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
-
-        final boolean drawBackgroundOnly = mTask == null || mTask.isLocked || mBitmapShader == null
-                || mThumbnailData == null;
-        if (drawBackgroundOnly) {
-            return;
-        }
-
         canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
     }
 
@@ -338,7 +344,6 @@
 
     private void updateThumbnailPaintFilter() {
         ColorFilter filter = getColorFilter(mDimAlpha);
-        mBackgroundPaint.setColorFilter(filter);
         int alpha = (int) (mDimAlpha * 255);
         mDimmingPaintAfterClearing.setAlpha(alpha);
         if (mBitmapShader != null) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index f716965..1d621dc 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -725,7 +725,8 @@
             TestLogging.recordEvent(
                     TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
             ActivityOptionsWrapper opts =  mActivity.getActivityLaunchOptions(this, null);
-            opts.options.setLaunchDisplayId(getRootViewDisplayId());
+            opts.options.setLaunchDisplayId(
+                    getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
             if (ActivityManagerWrapper.getInstance()
                     .startActivityFromRecents(mTask.key, opts.options)) {
                 RecentsView recentsView = getRecentsView();
@@ -766,7 +767,8 @@
             // Indicate success once the system has indicated that the transition has started
             ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
                     getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
-            opts.setLaunchDisplayId(getRootViewDisplayId());
+            opts.setLaunchDisplayId(
+                    getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
             if (freezeTaskList) {
                 ActivityOptionsCompat.setFreezeRecentTasksList(opts);
             }
@@ -912,31 +914,31 @@
     public void setOrientationState(RecentsOrientedState orientationState) {
         PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
         boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
-        snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+
         boolean isGridTask = isGridTask();
+        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
+
+        int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
         int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
         int taskMargin = isGridTask ? deviceProfile.overviewTaskMarginGridPx
                 : deviceProfile.overviewTaskMarginPx;
-        int taskIconMargin = snapshotParams.topMargin - taskIconHeight - taskMargin;
-        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        orientationHandler.setIconAndSnapshotParams(mIconView, taskIconMargin, taskIconHeight,
-                snapshotParams, isRtl);
-        updateDwbPlacement();
-        mSnapshotView.setLayoutParams(snapshotParams);
+        int taskIconMargin = thumbnailTopMargin - taskIconHeight - taskMargin;
+        orientationHandler.setTaskIconParams(iconParams, taskIconMargin, taskIconHeight,
+                thumbnailTopMargin, isRtl);
         iconParams.width = iconParams.height = taskIconHeight;
         mIconView.setLayoutParams(iconParams);
+
         mIconView.setRotation(orientationHandler.getDegreesRotated());
         int iconDrawableSize = isGridTask ? deviceProfile.overviewTaskIconDrawableSizeGridPx
                 : deviceProfile.overviewTaskIconDrawableSizePx;
         mIconView.setDrawableSize(iconDrawableSize, iconDrawableSize);
-        snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        mSnapshotView.setLayoutParams(snapshotParams);
-        mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
-    }
 
-    private void updateDwbPlacement() {
+        LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
+        snapshotParams.topMargin = thumbnailTopMargin;
+        mSnapshotView.setLayoutParams(snapshotParams);
+
+        mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
         mDigitalWellBeingToast.initialize(mTask);
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 4529217..9a39b34 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -42,6 +42,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.RemoteException;
+import android.platform.test.rule.CrashDetector;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -105,7 +106,8 @@
         }
 
         mOrderSensitiveRules = RuleChain
-                .outerRule(new NavigationModeSwitchRule(mLauncher))
+                .outerRule(new CrashDetector("com.android.systemui"))
+                .around(new NavigationModeSwitchRule(mLauncher))
                 .around(new FailureWatcher(mDevice, mLauncher));
 
         mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
diff --git a/res/color-night-v31/all_apps_button_color_2.xml b/res/color-night-v31/all_apps_button_color_2.xml
new file mode 100644
index 0000000..30b972f
--- /dev/null
+++ b/res/color-night-v31/all_apps_button_color_2.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent2_50"
+        android:lStar="98" />
+</selector>
diff --git a/res/color-v31/all_apps_button_bg_color.xml b/res/color-v31/all_apps_button_bg_color.xml
new file mode 100644
index 0000000..3ad38bc
--- /dev/null
+++ b/res/color-v31/all_apps_button_bg_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_neutral1_50"
+        android:lStar="98" />
+</selector>
diff --git a/res/color-v31/all_apps_button_color_1.xml b/res/color-v31/all_apps_button_color_1.xml
new file mode 100644
index 0000000..2d0895e
--- /dev/null
+++ b/res/color-v31/all_apps_button_color_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent1_50"
+        android:lStar="40" />
+</selector>
diff --git a/res/color-v31/all_apps_button_color_2.xml b/res/color-v31/all_apps_button_color_2.xml
new file mode 100644
index 0000000..7674b43
--- /dev/null
+++ b/res/color-v31/all_apps_button_color_2.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent2_50"
+        android:lStar="48" />
+</selector>
diff --git a/res/color-v31/all_apps_button_color_3.xml b/res/color-v31/all_apps_button_color_3.xml
new file mode 100644
index 0000000..17cb54f
--- /dev/null
+++ b/res/color-v31/all_apps_button_color_3.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent1_50"
+        android:lStar="35" />
+</selector>
diff --git a/res/color-v31/all_apps_button_color_4.xml b/res/color-v31/all_apps_button_color_4.xml
new file mode 100644
index 0000000..a6150f1
--- /dev/null
+++ b/res/color-v31/all_apps_button_color_4.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent3_50"
+        android:lStar="48" />
+</selector>
diff --git a/res/drawable/bg_all_apps_bottom_sheet.xml b/res/drawable/bg_all_apps_bottom_sheet.xml
deleted file mode 100644
index dba2fee..0000000
--- a/res/drawable/bg_all_apps_bottom_sheet.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2022 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<ripple android:color="?android:attr/colorControlHighlight"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-    <item>
-        <shape android:shape="rectangle"
-            android:tint="?colorButtonNormal">
-            <corners
-                android:topLeftRadius="@dimen/dialogCornerRadius"
-                android:topRightRadius="@dimen/dialogCornerRadius"/>
-            <solid android:color="?attr/allAppsScrimColor" />
-        </shape>
-    </item>
-</ripple>
\ No newline at end of file
diff --git a/res/drawable/bg_rounded_corner_bottom_sheet.xml b/res/drawable/bg_rounded_corner_bottom_sheet.xml
index aa49bce..dfcd354 100644
--- a/res/drawable/bg_rounded_corner_bottom_sheet.xml
+++ b/res/drawable/bg_rounded_corner_bottom_sheet.xml
@@ -16,7 +16,7 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle" >
-    <solid android:color="@color/surface" />
+    <solid android:color="?android:attr/colorBackground" />
     <corners
         android:topLeftRadius="@dimen/dialogCornerRadius"
         android:topRightRadius="@dimen/dialogCornerRadius" />
diff --git a/res/drawable/bg_rounded_corner_bottom_sheet_handle.xml b/res/drawable/bg_rounded_corner_bottom_sheet_handle.xml
new file mode 100644
index 0000000..c502178
--- /dev/null
+++ b/res/drawable/bg_rounded_corner_bottom_sheet_handle.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle" >
+    <solid android:color="?androidprv:attr/colorSurfaceVariant"/>
+    <corners android:radius="@dimen/bottom_sheet_handle_corner_radius" />
+</shape>
diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml
index 52b919b..5770d3c 100644
--- a/res/drawable/ic_all_apps_button.xml
+++ b/res/drawable/ic_all_apps_button.xml
@@ -18,26 +18,27 @@
     android:width="80dp"
     android:height="80dp"
     android:viewportWidth="80"
-    android:viewportHeight="80">
+    android:viewportHeight="80"
+    android:theme="@style/AllAppsTheme">
   <path
       android:pathData="M40,0.5L40,0.5c21.8,0 39.5,17.7 39.5,39.5l0,0c0,21.8 -17.7,39.5 -39.5,39.5l0,0C18.2,79.5 0.5,61.8 0.5,40l0,0C0.5,18.2 18.2,0.5 40,0.5z"
-      android:fillColor="#F7F9FA"/>
+      android:fillColor="?attr/allAppsButtonBgColor"/>
   <path
       android:pathData="M26.8,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="#00677E"/>
+      android:fillColor="?attr/allAppsButtonColor1"/>
   <path
       android:pathData="M26.8,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="#5F757E"/>
+      android:fillColor="?attr/allAppsButtonColor2"/>
   <path
       android:pathData="M40,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="#5F757E"/>
+      android:fillColor="?attr/allAppsButtonColor3"/>
   <path
       android:pathData="M40,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="#6C6F93"/>
+      android:fillColor="?attr/allAppsButtonColor2"/>
   <path
       android:pathData="M53.2,32.1m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="#005A6E"/>
+      android:fillColor="?attr/allAppsButtonColor4"/>
   <path
       android:pathData="M53.2,47.9m-5.3,0a5.3,5.3 0,1 1,10.6 0a5.3,5.3 0,1 1,-10.6 0"
-      android:fillColor="#5F757E"/>
+      android:fillColor="?attr/allAppsButtonColor2"/>
 </vector>
diff --git a/res/layout/all_apps_bottom_sheet_background.xml b/res/layout/all_apps_bottom_sheet_background.xml
index ad10d68..12b6b7b 100644
--- a/res/layout/all_apps_bottom_sheet_background.xml
+++ b/res/layout/all_apps_bottom_sheet_background.xml
@@ -17,19 +17,19 @@
     android:id="@+id/bottom_sheet_background"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/bg_all_apps_bottom_sheet">
+    android:background="@drawable/bg_rounded_corner_bottom_sheet">
 
     <View
         android:id="@+id/bottom_sheet_handle_area"
         android:layout_width="match_parent"
-        android:layout_height="34dp" />
+        android:layout_height="36dp" />
 
     <View
         android:id="@+id/bottom_sheet_handle"
-        android:layout_width="48dp"
-        android:layout_height="2dp"
+        android:layout_width="@dimen/bottom_sheet_handle_width"
+        android:layout_height="@dimen/bottom_sheet_handle_height"
         android:layout_gravity="center_horizontal"
-        android:layout_marginTop="16dp"
-        android:layout_marginBottom="16dp"
-        android:background="?android:attr/textColorSecondary" />
+        android:layout_marginTop="@dimen/bottom_sheet_handle_margin"
+        android:layout_marginBottom="@dimen/bottom_sheet_handle_margin"
+        android:background="@drawable/bg_rounded_corner_bottom_sheet_handle" />
 </FrameLayout>
diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml
index 8d47f4e..e4ca52e 100644
--- a/res/layout/floating_split_select_view.xml
+++ b/res/layout/floating_split_select_view.xml
@@ -4,7 +4,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <ImageView
+    <com.android.quickstep.views.FloatingTaskThumbnailView
         android:id="@+id/thumbnail"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -14,7 +14,6 @@
         android:id="@+id/split_placeholder"
         android:layout_width="match_parent"
         android:layout_height="@dimen/split_placeholder_size"
-        android:background="?android:colorPrimary"
         android:visibility="gone" />
 
 </com.android.quickstep.views.FloatingTaskView>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index 1a2cfc6..a5f72ef 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -19,16 +19,16 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/bg_rounded_corner_bottom_sheet"
-        android:paddingTop="16dp"
+        android:paddingTop="@dimen/bottom_sheet_handle_margin"
         android:orientation="vertical">
         <View
             android:id="@+id/collapse_handle"
-            android:layout_width="48dp"
-            android:layout_height="2dp"
+            android:layout_width="@dimen/bottom_sheet_handle_width"
+            android:layout_height="@dimen/bottom_sheet_handle_height"
             android:layout_gravity="center_horizontal"
-            android:layout_marginBottom="16dp"
+            android:layout_marginBottom="@dimen/bottom_sheet_handle_margin"
             android:visibility="gone"
-            android:background="?android:attr/textColorSecondary"/>
+            android:background="@drawable/bg_rounded_corner_bottom_sheet_handle"/>
         <TextView
             style="@style/TextHeadline"
             android:id="@+id/title"
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 309dc42..505ecb1 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -31,11 +31,11 @@
 
         <View
             android:id="@+id/collapse_handle"
-            android:layout_width="48dp"
-            android:layout_height="2dp"
-            android:layout_marginTop="16dp"
+            android:layout_width="@dimen/bottom_sheet_handle_width"
+            android:layout_height="@dimen/bottom_sheet_handle_height"
+            android:layout_marginTop="@dimen/bottom_sheet_handle_margin"
             android:layout_centerHorizontal="true"
-            android:background="?android:attr/textColorSecondary"/>
+            android:background="@drawable/bg_rounded_corner_bottom_sheet_handle"/>
 
         <TextView
             android:id="@+id/no_widgets_text"
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index a0c784f..1ddd652 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -103,7 +103,7 @@
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
     <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Hintergrund &amp; Stil"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Startbildschirm-Einstellungen"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Drehen des Startbildschirms zulassen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Beim Drehen des Smartphones"</string>
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
index 2c1bc90..eefe8c5 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -24,4 +24,9 @@
     <color name="home_settings_thumb_off_color">@android:color/system_neutral2_300</color>
     <color name="home_settings_track_on_color">@android:color/system_accent2_700</color>
     <color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
+
+    <color name="all_apps_button_bg_color">@android:color/system_neutral1_800</color>
+    <color name="all_apps_button_color_1">@android:color/system_accent1_300</color>
+    <color name="all_apps_button_color_3">@android:color/system_accent1_100</color>
+    <color name="all_apps_button_color_4">@android:color/system_accent2_100</color>
 </resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
new file mode 100644
index 0000000..ce272ce
--- /dev/null
+++ b/res/values-night/colors.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+/*
+* Copyright (C) 2022 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+    <color name="all_apps_button_bg_color">#2E3132</color>
+    <color name="all_apps_button_color_1">#33B9DB</color>
+    <color name="all_apps_button_color_2">#EFFBFF</color>
+    <color name="all_apps_button_color_3">#B1EBFF</color>
+    <color name="all_apps_button_color_4">#DEE0FF</color>
+</resources>
\ No newline at end of file
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 1149e02..5612191 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -53,9 +53,9 @@
     <string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
     <string name="widget_education_header" msgid="4874760613775913787">"ଉପଯୋଗୀ ସୂଚନା ଆପଣଙ୍କ ପାଖରେ ସହଜରେ ଉପଲବ୍ଧ"</string>
     <string name="widget_education_content" msgid="745542879510751525">"ଆପଗୁଡ଼ିକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ମୂଳସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string>
-    <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ୍ ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+    <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="widget_education_close_button" msgid="8676165703104836580">"ବୁଝିଗଲି"</string>
-    <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ୍ ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+    <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ୍‌ ଖୋଜନ୍ତୁ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ଆପ୍‌ ଲୋଡ୍‌ ହେଉଛି..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍‌ ମିଳିଲା ନାହିଁ"</string>
@@ -79,10 +79,10 @@
     <string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍‍ ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍‌ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"ହୋମ୍‌ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍‌ ପଢ଼ନ୍ତୁ"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"ହୋମରେ ସେଟିଙ୍ଗ ପଢ଼ିବାକୁ ଆପ ଏବଂ ଶର୍ଟକଟକୁ ଅନୁମତି ଦିଏ।"</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"ହୋମ୍‌ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍‌ ଲେଖନ୍ତୁ"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"ହୋମରେ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍‌ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟ ପଢ଼ନ୍ତୁ"</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"ହୋମରେ ସେଟିଂସ ଏବଂ ସର୍ଟକଟକୁ ପଢ଼ିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟ ଲେଖନ୍ତୁ"</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"ହୋମରେ ସେଟିଂସ ଏବଂ ସର୍ଟକଟକୁ ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"ଫୋନ୍‌ କଲ୍‌ କରିବାକୁ <xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
     <string name="gadget_error_text" msgid="740356548025791839">"ୱିଜେଟ୍ ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
     <string name="gadget_setup_text" msgid="8348374825537681407">"ୱିଜେଟ ସେଟିଂସ"</string>
@@ -103,7 +103,7 @@
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍‌"</string>
     <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ୱାଲପେପର୍ ଏବଂ ଷ୍ଟାଇଲ୍"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ୍‌ ସେଟିଂସ୍"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ ସେଟିଂସ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ହୋମ୍‌ ସ୍କ୍ରିନ୍ ବୁଲାଇବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ଯେତେବେଳେ ଫୋନକୁ ବୁଲାଯାଇଥାଏ"</string>
@@ -112,7 +112,7 @@
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"ବିଜ୍ଞପ୍ତି ଆକ୍ସେସ୍‌ ଆବଶ୍ୟକ ଅଟେ"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"ବିଜ୍ଞପ୍ତି ବିନ୍ଦୁ ଦେଖାଇବାକୁ, <xliff:g id="NAME">%1$s</xliff:g> ପାଇଁ ଆପ୍‌ ବିଜ୍ଞପ୍ତି ଅନ୍‌ କରନ୍ତୁ"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"ସେଟିଂସ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+    <string name="title_change_settings" msgid="1376365968844349552">"ସେଟିଂସ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"ବିଜ୍ଞପ୍ତି ଡଟ୍‌ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ହୋମ୍ ସ୍କ୍ରିନରେ ଆପ୍ ଆଇକନଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ନୂଆ ଆପ୍‌ ପାଇଁ"</string>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 3dde3f6..92f806e 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -22,7 +22,8 @@
     <dimen name="widget_list_horizontal_margin">32dp</dimen>
 
 <!-- AllApps -->
-    <dimen name="all_apps_bottom_sheet_horizontal_padding">32dp</dimen>
+    <dimen name="all_apps_search_bar_content_overlap">0dp</dimen>
+    <dimen name="all_apps_bottom_sheet_horizontal_padding">46dp</dimen>
 
 <!-- Fast scroll -->
     <dimen name="fastscroll_popup_width">75dp</dimen>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 0000000..a9e0fb8
--- /dev/null
+++ b/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at`
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+<!-- AllApps -->
+    <dimen name="all_apps_top_padding">0dp</dimen>
+</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 9d7941f..5c314d5 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,5 +16,6 @@
 
 <resources>
 <!-- AllApps -->
-    <dimen name="all_apps_bottom_sheet_horizontal_padding">41dp</dimen>
+    <dimen name="all_apps_top_padding">300dp</dimen>
+    <dimen name="all_apps_bottom_sheet_horizontal_padding">65dp</dimen>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 0b04b86..99a337e 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -54,6 +54,12 @@
     <attr name="workspaceAccentColor" format="color" />
     <attr name="dropTargetHoverTextColor" format="color" />
 
+    <attr name="allAppsButtonBgColor" format="color" />
+    <attr name="allAppsButtonColor1" format="color" />
+    <attr name="allAppsButtonColor2" format="color" />
+    <attr name="allAppsButtonColor3" format="color" />
+    <attr name="allAppsButtonColor4" format="color" />
+
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
         <attr name="layoutHorizontal" format="boolean" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 0b1b451..2bc9239 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -79,4 +79,10 @@
 
     <color name="workspace_accent_color_light">#ff8df5e3</color>
     <color name="workspace_accent_color_dark">#ff3d665f</color>
+
+    <color name="all_apps_button_bg_color">#F7F9FA</color>
+    <color name="all_apps_button_color_1">#00677E</color>
+    <color name="all_apps_button_color_2">#00677E</color>
+    <color name="all_apps_button_color_3">#5F757E</color>
+    <color name="all_apps_button_color_4">#005A6E</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ddc7d10..3e666fc 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -93,8 +93,11 @@
     <dimen name="fastscroll_end_margin">-26dp</dimen>
 
     <!-- All Apps -->
-    <dimen name="all_apps_open_vertical_translate">320dp</dimen>
+    <dimen name="all_apps_starting_vertical_translate">320dp</dimen>
+    <dimen name="all_apps_top_padding">0dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
+    <!-- all_apps_search_bar_field_height / 2 -->
+    <dimen name="all_apps_search_bar_content_overlap">24dp</dimen>
     <dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
@@ -363,4 +366,9 @@
     <dimen name="search_row_small_icon_size">32dp</dimen>
     <dimen name="padded_rounded_button_padding">8dp</dimen>
 
+<!-- Bottom sheet related parameters -->
+    <dimen name="bottom_sheet_handle_width">32dp</dimen>
+    <dimen name="bottom_sheet_handle_height">4dp</dimen>
+    <dimen name="bottom_sheet_handle_margin">16dp</dimen>
+    <dimen name="bottom_sheet_handle_corner_radius">2dp</dimen>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 818a032..864bb58 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -203,6 +203,14 @@
         <item name="android:importantForAccessibility">no</item>
     </style>
 
+    <style name="AllAppsButtonTheme">
+        <item name="allAppsButtonBgColor">@color/all_apps_button_bg_color</item>
+        <item name="allAppsButtonColor1">@color/all_apps_button_color_1</item>
+        <item name="allAppsButtonColor2">@color/all_apps_button_color_2</item>
+        <item name="allAppsButtonColor3">@color/all_apps_button_color_3</item>
+        <item name="allAppsButtonColor4">@color/all_apps_button_color_4</item>
+    </style>
+
     <style name="AllAppsTheme">
         <item name="disabledIconAlpha">.54</item>
     </style>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 11f58e7..ad4cae4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -1169,9 +1169,7 @@
         // Apply local extracted color if the DragView is an AppWidgetHostViewDrawable.
         View view = dragObject.dragView.getContentView();
         if (view instanceof LauncherAppWidgetHostView) {
-            Launcher launcher = Launcher.getLauncher(getContext());
-            Workspace workspace = launcher.getWorkspace();
-            int screenId = workspace.getIdForScreen(this);
+            int screenId = getWorkspace().getIdForScreen(this);
             cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
 
             ((LauncherAppWidgetHostView) view).handleDrag(mTempRect, this, screenId);
@@ -1184,11 +1182,24 @@
             return getContext().getString(R.string.move_to_hotseat_position,
                     Math.max(cellX, cellY) + 1);
         } else {
-            return getContext().getString(R.string.move_to_empty_cell,
-                    cellY + 1, cellX + 1);
+            Workspace workspace = getWorkspace();
+            int row = cellY + 1;
+            int col = workspace.mIsRtl ? mCountX - cellX : cellX + 1;
+            int panelCount = workspace.getPanelCount();
+            if (panelCount > 1) {
+                // Increment the column if the target is on the right side of a two panel home
+                int screenId = workspace.getIdForScreen(this);
+                int pageIndex = workspace.getPageIndexForScreenId(screenId);
+                col += (pageIndex % panelCount) * mCountX;
+            }
+            return getContext().getString(R.string.move_to_empty_cell, row, col);
         }
     }
 
+    private Workspace getWorkspace() {
+        return Launcher.cast(mActivity).getWorkspace();
+    }
+
     public void clearDragOutlines() {
         final int oldIndex = mDragOutlineCurrent;
         mDragOutlineAnims[oldIndex].animateOut();
@@ -2243,7 +2254,7 @@
     private void commitTempPlacement(View dragView) {
         mTmpOccupied.copyTo(mOccupied);
 
-        int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
+        int screenId = getWorkspace().getIdForScreen(this);
         int container = Favorites.CONTAINER_DESKTOP;
 
         if (mContainerType == HOTSEAT) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 62703ad..97c0f38 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -171,7 +171,8 @@
 
     // All apps
     public Point allAppsBorderSpacePx;
-    public int allAppsOpenVerticalTranslate;
+    public int allAppsShiftRange;
+    public int allAppsTopPadding;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
     public int allAppsIconSizePx;
@@ -244,8 +245,7 @@
         isTablet = info.isTablet(windowBounds);
         isPhone = !isTablet;
         isTwoPanels = isTablet && useTwoPanels;
-        isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
-                && FeatureFlags.ENABLE_TASKBAR.get();
+        isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS;
 
         // Some more constants.
         context = getContext(context, info, isVerticalBarLayout() || (isTablet && isLandscape)
@@ -288,8 +288,11 @@
         desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
         desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
 
-        allAppsOpenVerticalTranslate = res.getDimensionPixelSize(
-                R.dimen.all_apps_open_vertical_translate);
+        allAppsTopPadding = res.getDimensionPixelSize(R.dimen.all_apps_top_padding)
+                + (isTablet ? heightPx - availableHeightPx : 0);
+        allAppsShiftRange = isTablet
+                ? heightPx - allAppsTopPadding
+                : res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
 
         folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
         folderContentPaddingLeftRight =
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index a199a57..fdf0101 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index cb0cc11..e1680fc 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -173,9 +173,15 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        int width = mActivity.getDeviceProfile().isQsbInline
-                ? mActivity.getDeviceProfile().qsbWidth
-                : getShortcutsAndWidgets().getMeasuredWidth();
+        int width;
+        if (mActivity.getDeviceProfile().isQsbInline) {
+            width = mActivity.getDeviceProfile().qsbWidth;
+        } else {
+            MarginLayoutParams qsbParams = (MarginLayoutParams) mQsb.getLayoutParams();
+            width = getShortcutsAndWidgets().getMeasuredWidth()
+                    - qsbParams.getMarginStart()
+                    - qsbParams.getMarginEnd();
+        }
 
         mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 4300392..c43172c 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -67,18 +67,16 @@
             };
 
     /**
-     * Property to set the scale of workspace and hotseat. The value is based on a combination
+     * Property to set the scale of workspace. The value is based on a combination
      * of all the ones set, to have a smooth experience even in the case of overlapping scaling
      * animation.
      */
-    public static final MultiScalePropertyFactory<View> SCALE_PROPERTY_FACTORY =
-            new MultiScalePropertyFactory<View>("scale_property") {
-                @Override
-                protected void apply(View view, float scale) {
-                    view.setScaleX(scale);
-                    view.setScaleY(scale);
-                }
-            };
+    public static final MultiScalePropertyFactory<Workspace> WORKSPACE_SCALE_PROPERTY_FACTORY =
+            new MultiScalePropertyFactory<Workspace>("workspace_scale_property");
+
+    /** Property to set the scale of hotseat. */
+    public static final MultiScalePropertyFactory<Hotseat> HOTSEAT_SCALE_PROPERTY_FACTORY =
+            new MultiScalePropertyFactory<Hotseat>("hotseat_scale_property");
 
     public static final int SCALE_INDEX_UNFOLD_ANIMATION = 1;
     public static final int SCALE_INDEX_UNLOCK_ANIMATION = 2;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 16fecde..df97d2f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2484,21 +2484,27 @@
             }
         }
 
+        // Note, centerX represents the center of the object that is being dragged, visually. d.x
+        // represents the location of the finger within the dragged item.
+        float touchX;
+        float touchY = d.y;
+
+        // Go through the pages and check if the dragged item is inside one of them. This block
+        // is responsible for  determining whether we need to snap to a different screen.
         int nextPage = getNextPage();
-        IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage + 1);
-        if (isTwoPanelEnabled()) {
-            // If two panel is enabled, users can also drag items to nextPage + 2
-            pageIndexesToVerify.add(nextPage + 2);
-        }
-
-        int touchX = (int) Math.min(centerX, d.x);
-        int touchY = d.y;
-
-        // Go through the pages and check if the dragged item is inside one of them
+        IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage
+                + (isTwoPanelEnabled() ? 2 : 1));
         for (int pageIndex : pageIndexesToVerify) {
             if (layout != null || isPageInTransition()) {
                 break;
             }
+
+            // When deciding whether to perform a page switch, we need to consider the most extreme
+            // X coordinate between the finger location and the center of the object being dragged.
+            // This is either the max or the min of the two depending on whether dragging to the
+            // left / right, respectively.
+            touchX = ((((pageIndex < nextPage) && !mIsRtl) || pageIndex > nextPage && mIsRtl)
+                    ? Math.min(d.x, centerX) : Math.max(d.x, centerX));
             layout = verifyInsidePage(pageIndex, touchX, touchY);
         }
 
@@ -2507,12 +2513,16 @@
         // on one panel just choose the current page.
         if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
             if (isTwoPanelEnabled()) {
+                // When determining which panel to use within a single screen, we always use
+                // the centroid of the object rather than the finger.
+                touchX = centerX;
                 nextPage = getScreenCenter(getScrollX()) > touchX
                         ? (mIsRtl ? nextPage + 1 : nextPage) // left side
                         : (mIsRtl ? nextPage : nextPage + 1); // right side
             }
             layout = (CellLayout) getChildAt(nextPage);
         }
+
         if (layout != mDragTargetLayout) {
             setCurrentDropLayout(layout);
             setCurrentDragOverlappingLayout(layout);
@@ -3398,7 +3408,10 @@
             // When the workspace is not loaded, we do not know how many screen will be bound.
             return getContext().getString(R.string.home_screen);
         }
-        return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
+        int panelCount = getPanelCount();
+        int currentPage = (page / panelCount) + 1;
+        int totalPages = nScreens / panelCount + nScreens % panelCount;
+        return getContext().getString(R.string.workspace_scroll_format, currentPage, totalPages);
     }
 
     /**
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 98e785f..d94e84c 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,11 +18,12 @@
 
 import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE;
 
+import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WORKSPACE_STATE;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
@@ -64,8 +65,11 @@
  */
 public class WorkspaceStateTransitionAnimation {
 
-    private static final FloatProperty<View> WORKSPACE_STATE_SCALE_PROPERTY =
-            SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE);
+    private static final FloatProperty<Workspace> WORKSPACE_SCALE_PROPERTY =
+            WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE);
+
+    private static final FloatProperty<Hotseat> HOTSEAT_SCALE_PROPERTY =
+            HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE);
 
     private final Launcher mLauncher;
     private final Workspace mWorkspace;
@@ -120,9 +124,9 @@
                 && fromState == HINT_STATE && state == NORMAL;
         if (shouldSpring) {
             ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
-                    mWorkspace, mNewScale));
+                    mWorkspace, mNewScale, WORKSPACE_SCALE_PROPERTY));
         } else {
-            propertySetter.setFloat(mWorkspace, WORKSPACE_STATE_SCALE_PROPERTY, mNewScale,
+            propertySetter.setFloat(mWorkspace, WORKSPACE_SCALE_PROPERTY, mNewScale,
                     scaleInterpolator);
         }
 
@@ -130,11 +134,12 @@
         float hotseatScale = hotseatScaleAndTranslation.scale;
         if (shouldSpring) {
             PendingAnimation pa = (PendingAnimation) propertySetter;
-            pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
+            pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale,
+                    HOTSEAT_SCALE_PROPERTY));
         } else {
             Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
                     scaleInterpolator);
-            propertySetter.setFloat(hotseat, WORKSPACE_STATE_SCALE_PROPERTY, hotseatScale,
+            propertySetter.setFloat(hotseat, HOTSEAT_SCALE_PROPERTY, hotseatScale,
                     hotseatScaleInterpolator);
         }
 
@@ -198,9 +203,18 @@
     }
 
     /**
+     * Returns a spring based animator for the scale property of {@param workspace}.
+     */
+    public static ValueAnimator getWorkspaceSpringScaleAnimator(Launcher launcher,
+            Workspace workspace, float scale) {
+        return getSpringScaleAnimator(launcher, workspace, scale, WORKSPACE_SCALE_PROPERTY);
+    }
+
+    /**
      * Returns a spring based animator for the scale property of {@param v}.
      */
-    public static ValueAnimator getSpringScaleAnimator(Launcher launcher, View v, float scale) {
+    public static <T extends View> ValueAnimator getSpringScaleAnimator(Launcher launcher, T v,
+            float scale, FloatProperty<T> property) {
         ResourceProvider rp = DynamicResource.provider(launcher);
         float damping = rp.getFloat(R.dimen.hint_scale_damping_ratio);
         float stiffness = rp.getFloat(R.dimen.hint_scale_stiffness);
@@ -211,9 +225,9 @@
                 .setDampingRatio(damping)
                 .setMinimumVisibleChange(MIN_VISIBLE_CHANGE_SCALE)
                 .setEndValue(scale)
-                .setStartValue(WORKSPACE_STATE_SCALE_PROPERTY.get(v))
+                .setStartValue(property.get(v))
                 .setStartVelocity(velocityPxPerS)
-                .build(v, WORKSPACE_STATE_SCALE_PROPERTY);
+                .build(v, property);
 
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index b6a2459..cdc313f 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -89,15 +89,15 @@
     private float mShiftRange;      // changes depending on the orientation
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
 
-    private float mScrollRangeDelta = 0;
     private ScrimView mScrimView;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
-        mShiftRange = mLauncher.getDeviceProfile().heightPx;
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        setShiftRange(dp.allAppsShiftRange);
         mProgress = 1f;
 
-        mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
+        mIsVerticalLayout = dp.isVerticalBarLayout();
         mLauncher.addOnDeviceProfileChangeListener(this);
     }
 
@@ -108,7 +108,7 @@
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         mIsVerticalLayout = dp.isVerticalBarLayout();
-        setScrollRangeDelta(mScrollRangeDelta);
+        setShiftRange(dp.allAppsShiftRange);
 
         if (mIsVerticalLayout) {
             mLauncher.getHotseat().setTranslationY(0);
@@ -160,12 +160,14 @@
         }
 
         // need to decide depending on the release velocity
-        Interpolator interpolator = (config.userControlled ? LINEAR : DEACCEL_1_7);
-
+        Interpolator verticalProgressInterpolator = config.getInterpolator(ANIM_VERTICAL_PROGRESS,
+                config.userControlled ? LINEAR : DEACCEL_1_7);
         Animator anim = createSpringAnimation(mProgress, targetProgress);
-        anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
+        anim.setInterpolator(verticalProgressInterpolator);
         anim.addListener(getProgressAnimatorListener());
         builder.add(anim);
+        // Use ANIM_VERTICAL_PROGRESS's interpolator to determine state transition threshold.
+        builder.setInterpolator(verticalProgressInterpolator);
 
         setAlphas(toState, config, builder);
 
@@ -215,9 +217,8 @@
     /**
      * Updates the total scroll range but does not update the UI.
      */
-    public void setScrollRangeDelta(float delta) {
-        mScrollRangeDelta = delta;
-        mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
+    public void setShiftRange(float shiftRange) {
+        mShiftRange = shiftRange;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 59e21c0..bfc7515 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -408,7 +408,7 @@
         if (grid.isVerticalBarLayout()) {
             setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
         } else {
-            setPadding(0, grid.isTablet ? insets.top : 0, 0, 0);
+            setPadding(0, grid.allAppsTopPadding, 0, 0);
         }
 
         InsettableFrameLayout.dispatchInsets(this, insets);
@@ -765,4 +765,11 @@
                     && mVerticalFadingEdge);
         }
     }
+
+    /**
+     * Returns a view that denotes the visible part of all apps container view.
+     */
+    public View getVisibleContainerView() {
+        return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 30d33f9..8601819 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.allapps;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 
@@ -59,12 +58,4 @@
         }
         return super.onTouchEvent(ev);
     }
-
-    @Override
-    public void setInsets(Rect insets) {
-        super.setInsets(insets);
-        int allAppsStartingPositionY = mActivityContext.getDeviceProfile().availableHeightPx
-                - mActivityContext.getDeviceProfile().allAppsOpenVerticalTranslate;
-        mActivityContext.getAllAppsController().setScrollRangeDelta(allAppsStartingPositionY);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index ffc049b..4a886a4 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -82,7 +82,7 @@
         setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint()));
 
         mContentOverlap =
-                getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height) / 2;
+                getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_content_overlap);
     }
 
     @Override
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 85ca280..1cc0c21 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -19,7 +19,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 1e7b224..9c12abd 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -130,6 +130,23 @@
         }
     };
 
+    public static final Interpolator LINEAR_TELEPORT = t -> {
+        float startTeleport = 0.2f;
+        float endTeleport = 0.4f;
+        float teleportProgress = 0.5f;
+        float v;
+        if (t < startTeleport) {
+            v = LINEAR.getInterpolation(t);
+        } else if (t < endTeleport) {
+            v = Utilities.mapToRange(t, startTeleport, endTeleport, startTeleport,
+                    endTeleport + teleportProgress, ACCEL_DEACCEL);
+        } else {
+            v = LINEAR.getInterpolation(t) + teleportProgress;
+        }
+        v = Utilities.boundToRange(v, 0f, 1f);
+        return v;
+    };
+
     private static final float FAST_FLING_PX_MS = 10;
 
     public static Interpolator scrollInterpolatorForVelocity(float velocity) {
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 3ab893b..1300ce7 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -77,6 +77,13 @@
         addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders);
     }
 
+    /**
+     * Configures interpolator of the underlying AnimatorSet.
+     */
+    public void setInterpolator(TimeInterpolator interpolator) {
+        mAnim.setInterpolator(interpolator);
+    }
+
     @Override
     public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
         if (view == null || view.getAlpha() == alpha) {
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index bd52158..40fa0cf 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -25,7 +25,7 @@
 import androidx.annotation.FloatRange;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.window.RefreshRateTracker;
 
 /**
  * Utility class to build an object animator which follows the same path as a spring animation for
@@ -134,7 +134,7 @@
     }
 
     public SpringAnimationBuilder computeParams() {
-        int singleFrameMs = DisplayController.getSingleFrameMs(mContext);
+        int singleFrameMs = RefreshRateTracker.getSingleFrameMs(mContext);
         double naturalFreq = Math.sqrt(mStiffness);
         double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index a949e11..3458ed1 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -96,6 +96,13 @@
             getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false,
                     "Keep All Apps search bar at the bottom (but above keyboard if open)");
 
+    public static final BooleanFlag INJECT_WEB_TOP = new DeviceFlag("INJECT_WEB_TOP", false,
+            "Show web suggestions on top of the search results");
+
+    public static final BooleanFlag USE_FALLBACK_APP_SEARCH = new DeviceFlag(
+            "USE_FALLBACK_APP_SEARCH", false,
+            "Use fallback launcher app search results instead of AiAi app results");
+
     public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
             "COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
 
@@ -197,12 +204,6 @@
             "ENABLE_APP_PREDICTIONS_WHILE_VISIBLE", true, "Allows app "
             + "predictions to be updated while they are visible to the user.");
 
-    public static final BooleanFlag ENABLE_TASKBAR = getDebugFlag(
-            "ENABLE_TASKBAR", true, "Allows a system Taskbar to be shown on larger devices.");
-
-    public static final BooleanFlag ENABLE_TASKBAR_EDU = getDebugFlag("ENABLE_TASKBAR_EDU", true,
-            "Enables showing taskbar education the first time an app is opened.");
-
     public static final BooleanFlag ENABLE_TASKBAR_POPUP_MENU = getDebugFlag(
             "ENABLE_TASKBAR_POPUP_MENU", true, "Enables long pressing taskbar icons to show the"
                     + " popup menu.");
@@ -252,10 +253,18 @@
             "ENABLE_ALL_APPS_IN_TASKBAR", true,
             "Enables accessing All Apps from the system Taskbar.");
 
+    public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag(
+            "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
+            "Enables One Search box in Taskbar All Apps.");
+
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(
             "ENABLE_SPLIT_FROM_WORKSPACE", true,
             "Enable initiating split screen from workspace.");
 
+    public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(
+            "ENABLE_NEW_MIGRATION_LOGIC", true,
+            "Enable the new grid migration logic, keeping pages when src < dest");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 1e6342c..8916519 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -24,7 +24,7 @@
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 2f3d5d8..fd11b37 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -47,7 +47,6 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.GridSizeMigrationTaskV2;
 import com.android.launcher3.model.LoaderTask;
-import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
@@ -156,9 +155,10 @@
             PreviewContext previewContext = new PreviewContext(inflationContext, mIdp);
             new LoaderTask(
                     LauncherAppState.getInstance(previewContext),
-                    null,
+                    /* bgAllAppsList= */ null,
                     new BgDataModel(),
-                    new ModelDelegate(), null) {
+                    LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
+                    /* results= */ null) {
 
                 @Override
                 public void run() {
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 3129e2a..308a8f2 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -41,9 +41,9 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 
 import androidx.annotation.NonNull;
+import androidx.core.util.Pair;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherFiles;
@@ -339,6 +339,16 @@
             List<IconRequestInfo<T>> iconRequestInfos) {
         Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
                 iconRequestInfos.stream()
+                        .filter(iconRequest -> {
+                            if (iconRequest.itemInfo.getTargetComponent() != null) {
+                                return true;
+                            }
+                            Log.i(TAG,
+                                    "Skipping Item info with null component name: "
+                                            + iconRequest.itemInfo);
+                            iconRequest.itemInfo.bitmap = getDefaultIcon(iconRequest.itemInfo.user);
+                            return false;
+                        })
                         .collect(groupingBy(iconRequest ->
                                 Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));
 
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 0d2bc37..9604766 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -562,7 +562,7 @@
     }
 
     /**
-     * Helps to construct and write the log message.
+     * Helps to construct and log launcher event.
      */
     public interface StatsLogger {
 
@@ -662,6 +662,58 @@
     }
 
     /**
+     * Helps to construct and log latency event.
+     */
+    public interface StatsLatencyLogger {
+
+        enum LatencyType {
+            UNKNOWN(0),
+            COLD(1),
+            HOT(2);
+
+            private final int mId;
+
+            LatencyType(int id) {
+                this.mId = id;
+            }
+
+            public int getId() {
+                return mId;
+            }
+
+        }
+
+        /**
+         * Sets {@link InstanceId} of log message.
+         */
+        default StatsLatencyLogger withInstanceId(InstanceId instanceId) {
+            return this;
+        }
+
+
+        /**
+         * Sets latency of the event.
+         */
+        default StatsLatencyLogger withLatency(long latencyInMillis) {
+            return this;
+        }
+
+        /**
+         * Sets {@link LatencyType} of log message.
+         */
+        default StatsLatencyLogger withType(LatencyType type) {
+            return this;
+        }
+
+
+        /**
+         * Builds the final message and logs it as {@link EventEnum}.
+         */
+        default void log(EventEnum event) {
+        }
+    }
+
+    /**
      * Returns new logger object.
      */
     public StatsLogger logger() {
@@ -672,11 +724,27 @@
         return logger;
     }
 
+    /**
+     * Returns new latency logger object.
+     */
+    public StatsLatencyLogger latencyLogger() {
+        StatsLatencyLogger logger = createLatencyLogger();
+        if (mInstanceId != null) {
+            logger.withInstanceId(mInstanceId);
+        }
+        return logger;
+    }
+
     protected StatsLogger createLogger() {
         return new StatsLogger() {
         };
     }
 
+    protected StatsLatencyLogger createLatencyLogger() {
+        return new StatsLatencyLogger() {
+        };
+    }
+
     /**
      * Sets InstanceId to every new {@link StatsLogger} object returned by {@link #logger()} when
      * not-null.
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 08c3149..3e49d79 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -38,7 +38,7 @@
 /**
  * Utility class representing persisted grid properties.
  */
-public class DeviceGridState {
+public class DeviceGridState implements Comparable<DeviceGridState> {
 
     public static final String KEY_WORKSPACE_SIZE = "migration_src_workspace_size";
     public static final String KEY_HOTSEAT_COUNT = "migration_src_hotseat_count";
@@ -84,16 +84,16 @@
      */
     public LauncherEvent getWorkspaceSizeEvent() {
         if (!TextUtils.isEmpty(mGridSizeString)) {
-            switch (mGridSizeString.charAt(0)) {
-                case '6':
+            switch (getColumns()) {
+                case 6:
                     return LAUNCHER_GRID_SIZE_6;
-                case '5':
+                case 5:
                     return LAUNCHER_GRID_SIZE_5;
-                case '4':
+                case 4:
                     return LAUNCHER_GRID_SIZE_4;
-                case '3':
+                case 3:
                     return LAUNCHER_GRID_SIZE_3;
-                case '2':
+                case 2:
                     return LAUNCHER_GRID_SIZE_2;
             }
         }
@@ -119,4 +119,21 @@
         return mNumHotseat == other.mNumHotseat
                 && Objects.equals(mGridSizeString, other.mGridSizeString);
     }
+
+    public Integer getColumns() {
+        return Integer.parseInt(String.valueOf(mGridSizeString.charAt(0)));
+    }
+
+    public Integer getRows() {
+        return Integer.parseInt(String.valueOf(mGridSizeString.charAt(2)));
+    }
+
+    @Override
+    public int compareTo(DeviceGridState other) {
+        Integer size = getColumns() * getRows();
+        Integer otherSize = other.getColumns() * other.getRows();
+
+        return size.compareTo(otherSize);
+    }
+
 }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index ca680b7..74b0a6f 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -38,6 +38,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.LauncherPreviewRenderer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
@@ -225,13 +226,21 @@
             screens.add(screenId);
         }
 
+        boolean preservePages = false;
+        if (screens.isEmpty() && FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.get()) {
+            DeviceGridState srcDeviceState = new DeviceGridState(mContext);
+            DeviceGridState destDeviceState = new DeviceGridState(idp);
+            preservePages = destDeviceState.compareTo(srcDeviceState) >= 0
+                    && destDeviceState.getColumns() - srcDeviceState.getColumns() <= 2;
+        }
+
         // Then we place the items on the screens
         for (int screenId : screens) {
             if (DEBUG) {
                 Log.d(TAG, "Migrating " + screenId);
             }
             GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
-                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff);
+                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff, false);
             workspaceSolution.find();
             if (mWorkspaceDiff.isEmpty()) {
                 break;
@@ -243,10 +252,12 @@
         int screenId = mDestReader.mLastScreenId + 1;
         while (!mWorkspaceDiff.isEmpty()) {
             GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
-                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff);
+                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff,
+                    preservePages);
             workspaceSolution.find();
             screenId++;
         }
+
         return true;
     }
 
@@ -363,13 +374,15 @@
         private final int mScreenId;
         private final int mTrgX;
         private final int mTrgY;
-        private final List<DbEntry> mItemsToPlace;
+        private final List<DbEntry> mSortedItemsToPlace;
+        private final boolean mMatchingScreenIdOnly;
 
         private int mNextStartX;
         private int mNextStartY;
 
         GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
-                Context context, int screenId, int trgX, int trgY, List<DbEntry> itemsToPlace) {
+                Context context, int screenId, int trgX, int trgY, List<DbEntry> sortedItemsToPlace,
+                boolean matchingScreenIdOnly) {
             mDb = db;
             mSrcReader = srcReader;
             mDestReader = destReader;
@@ -386,13 +399,16 @@
                     mOccupied.markCells(entry, true);
                 }
             }
-            mItemsToPlace = itemsToPlace;
+            mSortedItemsToPlace = sortedItemsToPlace;
+            mMatchingScreenIdOnly = matchingScreenIdOnly;
         }
 
         public void find() {
-            Iterator<DbEntry> iterator = mItemsToPlace.iterator();
+            Iterator<DbEntry> iterator = mSortedItemsToPlace.iterator();
             while (iterator.hasNext()) {
                 final DbEntry entry = iterator.next();
+                if (mMatchingScreenIdOnly && entry.screenId < mScreenId) continue;
+                if (mMatchingScreenIdOnly && entry.screenId > mScreenId) break;
                 if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
                     iterator.remove();
                     continue;
@@ -494,7 +510,7 @@
         private final SQLiteDatabase mDb;
         private final String mTableName;
         private final Context mContext;
-        private final HashSet<String> mValidPackages;
+        private final Set<String> mValidPackages;
         private int mLastScreenId = -1;
 
         private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
@@ -503,7 +519,7 @@
                 new ArrayMap<>();
 
         DbReader(SQLiteDatabase db, String tableName, Context context,
-                HashSet<String> validPackages) {
+                Set<String> validPackages) {
             mDb = db;
             mTableName = tableName;
             mContext = context;
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
index 60ca63b..cc42258 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -44,11 +44,7 @@
             boolean isPrimaryInstance) {
         ModelDelegate delegate = Overrides.getObject(
                 ModelDelegate.class, context, R.string.model_delegate_class);
-        delegate.mApp = app;
-        delegate.mAppsList = appsList;
-        delegate.mDataModel = dataModel;
-        delegate.mIsPrimaryInstance = isPrimaryInstance;
-        delegate.mContext = context;
+        delegate.init(context, app, appsList, dataModel, isPrimaryInstance);
         return delegate;
     }
 
@@ -61,6 +57,18 @@
     public ModelDelegate() { }
 
     /**
+     * Initializes the object with the given params.
+     */
+    private void init(Context context, LauncherAppState app, AllAppsList appsList,
+            BgDataModel dataModel, boolean isPrimaryInstance) {
+        this.mApp = app;
+        this.mAppsList = appsList;
+        this.mDataModel = dataModel;
+        this.mIsPrimaryInstance = isPrimaryInstance;
+        this.mContext = context;
+    }
+
+    /**
      * Called periodically to validate and update any data
      */
     @WorkerThread
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 05d6fc6..2fa7945 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -508,7 +508,9 @@
     @Override
     protected void closeComplete() {
         super.closeComplete();
-        mActivityContext.getDragController().removeDragListener(this);
+        if (mActivityContext.getDragController() != null) {
+            mActivityContext.getDragController().removeDragListener(this);
+        }
         PopupContainerWithArrow openPopup = getOpen(mActivityContext);
         if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
             mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 61d488c..c00e174 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -26,7 +26,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.animation.Animator.AnimatorListener;
 import android.animation.ValueAnimator;
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 989a9e4..f7d3492 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -17,9 +17,13 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.LINEAR_TELEPORT;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
@@ -94,9 +98,9 @@
             LauncherState toState) {
         StateAnimationConfig config = super.getConfigForStates(fromState, toState);
         if (fromState == NORMAL && toState == ALL_APPS) {
-            applyNormalToAllAppsAnimConfig(config);
+            applyNormalToAllAppsAnimConfig(mLauncher, config);
         } else if (fromState == ALL_APPS && toState == NORMAL) {
-            applyAllAppsToNormalConfig(config);
+            applyAllAppsToNormalConfig(mLauncher, config);
         }
         return config;
     }
@@ -104,17 +108,24 @@
     /**
      * Applies Animation config values for transition from all apps to home
      */
-    public static void applyAllAppsToNormalConfig(StateAnimationConfig config) {
+    public static void applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config) {
+        boolean isTablet = launcher.getDeviceProfile().isTablet;
         config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
-        config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+        config.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
+                ? FINAL_FRAME : ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+        config.setInterpolator(ANIM_VERTICAL_PROGRESS, isTablet ? LINEAR_TELEPORT : LINEAR);
     }
 
     /**
      * Applies Animation config values for transition from home to all apps
      */
-    public static void applyNormalToAllAppsAnimConfig(StateAnimationConfig config) {
+    public static void applyNormalToAllAppsAnimConfig(Launcher launcher,
+            StateAnimationConfig config) {
+        boolean isTablet = launcher.getDeviceProfile().isTablet;
         config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
-        config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+        config.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
+                ? INSTANT : ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+        config.setInterpolator(ANIM_VERTICAL_PROGRESS, isTablet ? LINEAR_TELEPORT : LINEAR);
     }
 
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 4a55d2e..2609e54 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -178,18 +178,6 @@
     }
 
     @Override
-    public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
-            DeviceProfile dp) {
-        // Don't use device profile here because we know we're in fake landscape, only split option
-        // available is top/left
-        if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
-            // Top (visually left) side
-            return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
-        }
-        throw new IllegalStateException("Invalid split stage position: " + stagePosition);
-    }
-
-    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollY();
     }
@@ -469,14 +457,12 @@
     }
 
     @Override
-    public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
-        FrameLayout.LayoutParams iconParams =
-                (FrameLayout.LayoutParams) iconView.getLayoutParams();
+    public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
+            int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
         iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
         iconParams.leftMargin = 0;
-        iconParams.topMargin = snapshotParams.topMargin / 2;
+        iconParams.topMargin = thumbnailTopMargin / 2;
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 923dcc6..6e594e9 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -47,10 +47,6 @@
  */
 public interface PagedOrientationHandler {
 
-    int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
-    int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
-    int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
-
     PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
     PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
     PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -82,12 +78,6 @@
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
 
-    /**
-     * @param stagePosition The position where the view to be split will go
-     * @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
-     * dismissal should happen
-     */
-    int getSplitTaskViewDismissDirection(@StagePosition int stagePosition, DeviceProfile dp);
     int getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
@@ -152,8 +142,8 @@
             StagedSplitBounds splitBoundsConfig, DeviceProfile dp);
 
     // Overview TaskMenuView methods
-    void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl);
+    void setTaskIconParams(FrameLayout.LayoutParams iconParams,
+            int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl);
     void setSplitIconParams(View primaryIconView, View secondaryIconView,
             int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
             boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 0d92e25..2c9afd6 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -180,24 +180,6 @@
     }
 
     @Override
-    public int getSplitTaskViewDismissDirection(@StagePosition int stagePosition,
-            DeviceProfile dp) {
-        if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
-            if (dp.isLandscape) {
-                // Left side
-                return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
-            } else {
-                // Top side
-                return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
-            }
-        } else if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
-            // We don't have a bottom option, so should be right
-            return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
-        }
-        throw new IllegalStateException("Invalid split stage position: " + stagePosition);
-    }
-
-    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollX();
     }
@@ -567,10 +549,8 @@
     }
 
     @Override
-    public void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
-        FrameLayout.LayoutParams iconParams =
-                (FrameLayout.LayoutParams) iconView.getLayoutParams();
+    public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
+            int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = TOP | CENTER_HORIZONTAL;
         iconParams.leftMargin = iconParams.rightMargin = 0;
         iconParams.topMargin = taskIconMargin;
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 69e19f4..9151796 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -23,7 +23,6 @@
 
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
@@ -168,14 +167,12 @@
     }
 
     @Override
-    public void setIconAndSnapshotParams(View mIconView, int taskIconMargin, int taskIconHeight,
-            FrameLayout.LayoutParams snapshotParams, boolean isRtl) {
-        FrameLayout.LayoutParams iconParams =
-                (FrameLayout.LayoutParams) mIconView.getLayoutParams();
+    public void setTaskIconParams(FrameLayout.LayoutParams iconParams,
+            int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
         iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
         iconParams.leftMargin = -taskIconHeight - taskIconMargin / 2;
         iconParams.rightMargin = 0;
-        iconParams.topMargin = snapshotParams.topMargin / 2;
+        iconParams.topMargin = thumbnailTopMargin / 2;
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index e8941e6..fe0bf6d 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -121,6 +121,9 @@
             mLongPressState = STATE_COMPLETED;
         }
 
+        boolean isInAllAppsBottomSheet = mLauncher.isInState(ALL_APPS)
+                && mLauncher.getDeviceProfile().isTablet;
+
         final boolean result;
         if (mLongPressState == STATE_COMPLETED) {
             // We have handled the touch, so workspace does not need to know anything anymore.
@@ -136,8 +139,9 @@
 
             result = true;
         } else {
-            // We don't want to handle touch, let workspace handle it as usual.
-            result = false;
+            // We don't want to handle touch unless we're in AllApps bottom sheet, let workspace
+            // handle it as usual.
+            result = isInAllAppsBottomSheet;
         }
 
         if (action == ACTION_UP || action == ACTION_POINTER_UP) {
@@ -153,9 +157,7 @@
         if (action == ACTION_UP || action == ACTION_CANCEL) {
             cancelLongPress();
         }
-        if (action == ACTION_UP
-                && mLauncher.isInState(ALL_APPS)
-                && mLauncher.getDeviceProfile().isTablet) {
+        if (action == ACTION_UP && isInAllAppsBottomSheet) {
             mLauncher.getStateManager().goToState(NORMAL);
             mLauncher.getStatsLogManager().logger()
                     .withSrcState(ALL_APPS.statsLogOrdinal)
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index ba925f5..7963401 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -20,7 +20,6 @@
 
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import static java.util.Collections.emptyMap;
@@ -34,7 +33,6 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -44,7 +42,6 @@
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.ApiWrapper;
@@ -58,7 +55,7 @@
  * Utility class to cache properties of default display to avoid a system RPC on every call.
  */
 @SuppressLint("NewApi")
-public class DisplayController implements DisplayListener, ComponentCallbacks, SafeCloseable {
+public class DisplayController implements ComponentCallbacks, SafeCloseable {
 
     private static final String TAG = "DisplayController";
 
@@ -102,7 +99,6 @@
         }
         mInfo = new Info(getDisplayInfoContext(display), display,
                 getInternalDisplays(mDM), emptyMap());
-        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
     }
 
     private static ArrayMap<String, PortraitSize> getInternalDisplays(
@@ -128,35 +124,6 @@
         } else {
             // TODO: unregister broadcast receiver
         }
-        mDM.unregisterDisplayListener(this);
-    }
-
-    @Override
-    public final void onDisplayAdded(int displayId) { }
-
-    @Override
-    public final void onDisplayRemoved(int displayId) { }
-
-    @WorkerThread
-    @Override
-    public final void onDisplayChanged(int displayId) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
-        Display display = mDM.getDisplay(DEFAULT_DISPLAY);
-        if (display == null) {
-            return;
-        }
-        if (Utilities.ATLEAST_S) {
-            // Only update refresh rate. Everything else comes from component callbacks
-            mInfo.mSingleFrameMs = getSingleFrameMs(display);
-            return;
-        }
-        handleInfoChange(display);
-    }
-
-    public static int getSingleFrameMs(Context context) {
-        return INSTANCE.get(context).getInfo().mSingleFrameMs;
     }
 
     /**
@@ -287,8 +254,6 @@
 
     public static class Info {
 
-        private int mSingleFrameMs;
-
         // Configuration properties
         public final int rotation;
         public final float fontScale;
@@ -318,7 +283,6 @@
             densityDpi = config.densityDpi;
             mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
 
-            mSingleFrameMs = getSingleFrameMs(display);
             currentSize = new Point();
             display.getRealSize(currentSize);
 
@@ -400,9 +364,4 @@
             return Objects.hash(width, height);
         }
     }
-
-    private static int getSingleFrameMs(Display display) {
-        float refreshRate = display.getRefreshRate();
-        return refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
-    }
 }
diff --git a/src/com/android/launcher3/util/MultiScalePropertyFactory.java b/src/com/android/launcher3/util/MultiScalePropertyFactory.java
index f27d0f0..a7e6cc8 100644
--- a/src/com/android/launcher3/util/MultiScalePropertyFactory.java
+++ b/src/com/android/launcher3/util/MultiScalePropertyFactory.java
@@ -18,6 +18,8 @@
 
 import android.util.ArrayMap;
 import android.util.FloatProperty;
+import android.util.Log;
+import android.view.View;
 
 import com.android.launcher3.Utilities;
 
@@ -33,8 +35,10 @@
  *
  * @param <T> Type where to apply the property.
  */
-public abstract class MultiScalePropertyFactory<T> {
+public class MultiScalePropertyFactory<T extends View> {
 
+    private static final boolean DEBUG = false;
+    private static final String TAG = "MultiScaleProperty";
     private final String mName;
     private final ArrayMap<Integer, MultiScaleProperty> mProperties =
             new ArrayMap<Integer, MultiScaleProperty>();
@@ -56,7 +60,6 @@
                 (k) -> new MultiScaleProperty(index, mName + "_" + index));
     }
 
-
     /**
      * Each [setValue] will be aggregated with the other properties values created by the
      * corresponding factory.
@@ -91,11 +94,22 @@
             mLastAggregatedValue = Utilities.boundToRange(multValue, minValue, maxValue);
             mValue = newValue;
             apply(obj, mLastAggregatedValue);
+
+            if (DEBUG) {
+                Log.d(TAG, "name=" + mName
+                        + " newValue=" + newValue + " mInx=" + mInx
+                        + " aggregated=" + mLastAggregatedValue + " others= " + mProperties);
+            }
         }
 
         @Override
-        public Float get(T t) {
-            return mLastAggregatedValue;
+        public Float get(T view) {
+            // The scale of the view should match mLastAggregatedValue. Still, if it has been
+            // changed without using this property, it can differ. As this get method is usually
+            // used to set the starting point on an animation, this would result in some jumps
+            // when the view scale is different than the last aggregated value. To stay on the
+            // safe side, let's return the real view scale.
+            return view.getScaleX();
         }
 
         @Override
@@ -104,6 +118,8 @@
         }
     }
 
-    /** Applies value to object after setValue method is called. */
-    protected abstract void apply(T obj, float value);
+    protected void apply(View view, float value) {
+        view.setScaleX(value);
+        view.setScaleY(value);
+    }
 }
diff --git a/src/com/android/launcher3/util/window/RefreshRateTracker.java b/src/com/android/launcher3/util/window/RefreshRateTracker.java
new file mode 100644
index 0000000..7814617
--- /dev/null
+++ b/src/com/android/launcher3/util/window/RefreshRateTracker.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util.window;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.view.Display;
+
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
+
+/**
+ * Utility class to track refresh rate of the current device
+ */
+public class RefreshRateTracker implements DisplayListener, SafeCloseable {
+
+    private static final MainThreadInitializedObject<RefreshRateTracker> INSTANCE =
+            new MainThreadInitializedObject<>(RefreshRateTracker::new);
+
+    private int mSingleFrameMs = 1;
+
+    private final DisplayManager mDM;
+
+    private RefreshRateTracker(Context context) {
+        mDM = context.getSystemService(DisplayManager.class);
+        updateSingleFrameMs();
+        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+    }
+
+    /**
+     * Returns the single frame time in ms
+     */
+    public static int getSingleFrameMs(Context context) {
+        return INSTANCE.get(context).mSingleFrameMs;
+    }
+
+    @Override
+    public final void onDisplayAdded(int displayId) { }
+
+    @Override
+    public final void onDisplayRemoved(int displayId) { }
+
+    @WorkerThread
+    @Override
+    public final void onDisplayChanged(int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            updateSingleFrameMs();
+        }
+    }
+
+    private void updateSingleFrameMs() {
+        Display display = mDM.getDisplay(DEFAULT_DISPLAY);
+        if (display != null) {
+            float refreshRate = display.getRefreshRate();
+            mSingleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+        }
+    }
+
+    @Override
+    public void close() {
+        mDM.unregisterDisplayListener(this);
+    }
+}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index c22d60d..5d88884 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -113,9 +113,16 @@
         return -1;
     }
 
+    /**
+     * Returns the range in height that the slide in view can be dragged.
+     */
+    protected float getShiftRange() {
+        return mContent.getHeight();
+    }
+
     protected void setTranslationShift(float translationShift) {
         mTranslationShift = translationShift;
-        mContent.setTranslationY(mTranslationShift * mContent.getHeight());
+        mContent.setTranslationY(mTranslationShift * getShiftRange());
         if (mColorScrim != null) {
             mColorScrim.setAlpha(1 - mTranslationShift);
         }
@@ -132,8 +139,7 @@
         mSwipeDetector.setDetectableScrollConditions(
                 directionsToDetectScroll, false);
         mSwipeDetector.onTouchEvent(ev);
-        return mSwipeDetector.isDraggingOrSettling()
-                || !getPopupContainer().isEventOverView(mContent, ev);
+        return mSwipeDetector.isDraggingOrSettling() || !isEventOverContent(ev);
     }
 
     @Override
@@ -142,13 +148,23 @@
         if (ev.getAction() == MotionEvent.ACTION_UP && mSwipeDetector.isIdleState()
                 && !isOpeningAnimationRunning()) {
             // If we got ACTION_UP without ever starting swipe, close the panel.
-            if (!getPopupContainer().isEventOverView(mContent, ev)) {
+            if (!isEventOverContent(ev)) {
                 close(true);
             }
         }
         return true;
     }
 
+    /**
+     * Returns {@code true} if the touch event is over the visible area of the bottom sheet.
+     *
+     * By default will check if the touch event is over {@code mContent}, subclasses should override
+     * this method if the visible area of the bottom sheet is different from {@code mContent}.
+     */
+    protected boolean isEventOverContent(MotionEvent ev) {
+        return getPopupContainer().isEventOverView(mContent, ev);
+    }
+
     private boolean isOpeningAnimationRunning() {
         return mIsOpen && mOpenCloseAnimator.isRunning();
     }
@@ -160,7 +176,7 @@
 
     @Override
     public boolean onDrag(float displacement) {
-        float range = mContent.getHeight();
+        float range = getShiftRange();
         displacement = Utilities.boundToRange(displacement, 0, range);
         setTranslationShift(displacement / range);
         return true;
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
index f502d46..b1e69c7 100644
--- a/src/com/android/launcher3/views/AllAppsButton.java
+++ b/src/com/android/launcher3/views/AllAppsButton.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.LauncherAppState;
@@ -40,8 +41,9 @@
     public AllAppsButton(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
+        Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
         Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
-                .createScaledBitmapWithShadow(context.getDrawable(R.drawable.ic_all_apps_button));
+                .createScaledBitmapWithShadow(theme.getDrawable(R.drawable.ic_all_apps_button));
         setIcon(new FastBitmapDrawable(bitmap));
     }
 }
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 76dfb3c..f71aa13 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -20,7 +20,7 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
 import android.annotation.TargetApi;
 import android.app.WallpaperManager;
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index e7cb3b3..7f54d6d 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -40,8 +40,8 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.window.RefreshRateTracker;
 
 /**
  * Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes
@@ -97,7 +97,7 @@
 
         // Remove after some time, to avoid flickering
         Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
-                DisplayController.getSingleFrameMs(mLauncher));
+                RefreshRateTracker.getSingleFrameMs(mLauncher));
     }
 
     private void removeViewFromParent() {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index 5543cc2..8a435c9 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -55,8 +55,15 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(1f, 0,
-                -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT);
+        ScaleAndTranslation scaleAndTranslation =
+                new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
+        if (launcher.getDeviceProfile().isTablet) {
+            scaleAndTranslation.scale = 0.97f;
+        } else {
+            scaleAndTranslation.translationY =
+                    -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT;
+        }
+        return scaleAndTranslation;
     }
 
     @Override
diff --git a/tests/Android.bp b/tests/Android.bp
index c2c545b..7542d04 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -67,7 +67,8 @@
         "androidx.test.uiautomator_uiautomator",
         "mockito-target-inline-minus-junit4",
         "launcher_log_protos_lite",
-        "truth-prebuilt"
+        "truth-prebuilt",
+        "platform-test-rules",
     ],
     manifest: "AndroidManifest-common.xml",
     platform_apis: true,
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index e44c172..b4cd773 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -267,6 +267,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity-alias android:name="Activity15" android:exported="true"
+            android:label="ThemeIconTestActivity"
+            android:icon="@drawable/test_theme_icon"
+            android:targetActivity="com.android.launcher3.testcomponent.OtherBaseTestingActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
 
         <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
         <provider
diff --git a/tests/res/drawable/test_theme_icon.xml b/tests/res/drawable/test_theme_icon.xml
new file mode 100644
index 0000000..be5c4d9
--- /dev/null
+++ b/tests/res/drawable/test_theme_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2021 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@android:color/white"/>
+    <foreground>
+        <color android:color="#FFFF0000" />
+    </foreground>
+    <monochrome>
+        <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+            <path
+                android:fillColor="#FF000000"
+                android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+        </vector>
+    </monochrome>
+</adaptive-icon>
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index f34a29e..92e3e64 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -34,6 +34,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 
 /**
@@ -44,6 +45,8 @@
 public class PromiseIconUiTest extends AbstractLauncherUiTest {
 
     private int mSessionId = -1;
+    // TODO(b/202985412): Revert to default timeout when PackageManager bug is fixed.
+    private static final long PROMISE_ICON_TIMEOUT = TimeUnit.SECONDS.toMillis(60);
 
     @Override
     public void setUp() throws Exception {
@@ -85,7 +88,8 @@
 
         // Verify promise icon is added
         waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
-                launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) != null,
+                PROMISE_ICON_TIMEOUT);
 
         // Remove session
         mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
@@ -93,7 +97,8 @@
 
         // Verify promise icon is removed
         waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
-                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null,
+                PROMISE_ICON_TIMEOUT);
     }
 
     @Test
@@ -111,6 +116,7 @@
 
         // Verify promise icon is not added
         waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
-                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+                launcher.getWorkspace().getFirstMatch(findPromiseApp) == null,
+                PROMISE_ICON_TIMEOUT);
     }
 }
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
deleted file mode 100644
index 005389e..0000000
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.model;
-
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-import static com.android.launcher3.LauncherSettings.Favorites.TMP_CONTENT_URI;
-import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
-import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
-import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
-import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
-import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-import android.os.Process;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.LauncherModelHelper;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashMap;
-import java.util.HashSet;
-
-/** Unit tests for {@link GridSizeMigrationTaskV2} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GridSizeMigrationTaskV2Test {
-
-    private LauncherModelHelper mModelHelper;
-    private Context mContext;
-    private SQLiteDatabase mDb;
-
-    private HashSet<String> mValidPackages;
-    private InvariantDeviceProfile mIdp;
-
-    private final String testPackage1 = "com.android.launcher3.validpackage1";
-    private final String testPackage2 = "com.android.launcher3.validpackage2";
-    private final String testPackage3 = "com.android.launcher3.validpackage3";
-    private final String testPackage4 = "com.android.launcher3.validpackage4";
-    private final String testPackage5 = "com.android.launcher3.validpackage5";
-    private final String testPackage6 = "com.android.launcher3.validpackage6";
-    private final String testPackage7 = "com.android.launcher3.validpackage7";
-    private final String testPackage8 = "com.android.launcher3.validpackage8";
-    private final String testPackage9 = "com.android.launcher3.validpackage9";
-    private final String testPackage10 = "com.android.launcher3.validpackage10";
-
-    @Before
-    public void setUp() {
-        mModelHelper = new LauncherModelHelper();
-        mContext = mModelHelper.sandboxContext;
-        mDb = mModelHelper.provider.getDb();
-
-        mValidPackages = new HashSet<>();
-        mValidPackages.add(TEST_PACKAGE);
-        mValidPackages.add(testPackage1);
-        mValidPackages.add(testPackage2);
-        mValidPackages.add(testPackage3);
-        mValidPackages.add(testPackage4);
-        mValidPackages.add(testPackage5);
-        mValidPackages.add(testPackage6);
-        mValidPackages.add(testPackage7);
-        mValidPackages.add(testPackage8);
-        mValidPackages.add(testPackage9);
-        mValidPackages.add(testPackage10);
-
-        mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
-
-        long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
-                Process.myUserHandle());
-        dropTable(mDb, LauncherSettings.Favorites.TMP_TABLE);
-        LauncherSettings.Favorites.addTableToDb(mDb, userSerial, false,
-                LauncherSettings.Favorites.TMP_TABLE);
-    }
-
-    @After
-    public void tearDown() {
-        mModelHelper.destroy();
-    }
-
-    @Test
-    public void testMigration() throws Exception {
-        int[] srcHotseatItems = {
-                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
-                mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
-                -1,
-                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
-                mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
-        };
-        mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage5, 5, TMP_CONTENT_URI);
-        mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage6, 6, TMP_CONTENT_URI);
-        mModelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage8, 8, TMP_CONTENT_URI);
-        mModelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage9, 9, TMP_CONTENT_URI);
-        mModelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage10, 10, TMP_CONTENT_URI);
-
-        int[] destHotseatItems = {
-                -1,
-                mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2),
-                -1,
-        };
-        mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
-
-        mIdp.numDatabaseHotseatIcons = 4;
-        mIdp.numColumns = 4;
-        mIdp.numRows = 4;
-        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages);
-        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages);
-        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
-                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
-        task.migrate(mIdp);
-
-        // Check hotseat items
-        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_HOTSEAT, null, LauncherSettings.Favorites.SCREEN, null);
-        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
-        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
-        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 0);
-        assertTrue(c.getString(intentIndex).contains(testPackage1));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 1);
-        assertTrue(c.getString(intentIndex).contains(testPackage2));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 2);
-        assertTrue(c.getString(intentIndex).contains(testPackage3));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 3);
-        assertTrue(c.getString(intentIndex).contains(testPackage4));
-        c.close();
-
-        // Check workspace items
-        c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
-                        LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_DESKTOP, null, null, null);
-        intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
-        int cellXIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLX);
-        int cellYIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLY);
-
-        HashMap<String, Point> locMap = new HashMap<>();
-        while (c.moveToNext()) {
-            locMap.put(
-                    Intent.parseUri(c.getString(intentIndex), 0).getPackage(),
-                    new Point(c.getInt(cellXIndex), c.getInt(cellYIndex)));
-        }
-        c.close();
-
-        assertEquals(locMap.size(), 6);
-        assertEquals(new Point(0, 2), locMap.get(testPackage8));
-        assertEquals(new Point(0, 3), locMap.get(testPackage6));
-        assertEquals(new Point(1, 3), locMap.get(testPackage10));
-        assertEquals(new Point(2, 3), locMap.get(testPackage5));
-        assertEquals(new Point(3, 3), locMap.get(testPackage9));
-    }
-
-    @Test
-    public void migrateToLargerHotseat() {
-        int[] srcHotseatItems = {
-                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
-                mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
-                mModelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
-                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
-        };
-
-        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
-        mIdp.numDatabaseHotseatIcons = 6;
-        mIdp.numColumns = 4;
-        mIdp.numRows = 4;
-        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages);
-        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages);
-        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
-                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
-        task.migrate(mIdp);
-
-        // Check hotseat items
-        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_HOTSEAT, null, LauncherSettings.Favorites.SCREEN, null);
-        assertEquals(c.getCount(), numSrcDatabaseHotseatIcons);
-        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
-        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 0);
-        assertTrue(c.getString(intentIndex).contains(testPackage1));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 1);
-        assertTrue(c.getString(intentIndex).contains(testPackage2));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 2);
-        assertTrue(c.getString(intentIndex).contains(testPackage3));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 3);
-        assertTrue(c.getString(intentIndex).contains(testPackage4));
-
-        c.close();
-    }
-
-    @Test
-    public void migrateFromLargerHotseat() {
-        int[] srcHotseatItems = {
-                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
-                -1,
-                mModelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
-                mModelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
-                mModelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
-                mModelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI),
-        };
-
-        mIdp.numDatabaseHotseatIcons = 4;
-        mIdp.numColumns = 4;
-        mIdp.numRows = 4;
-        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages);
-        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
-                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages);
-        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
-                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
-        task.migrate(mIdp);
-
-        // Check hotseat items
-        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
-                "container=" + CONTAINER_HOTSEAT, null, LauncherSettings.Favorites.SCREEN, null);
-        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
-        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
-        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 0);
-        assertTrue(c.getString(intentIndex).contains(testPackage1));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 1);
-        assertTrue(c.getString(intentIndex).contains(testPackage2));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 2);
-        assertTrue(c.getString(intentIndex).contains(testPackage3));
-        c.moveToNext();
-        assertEquals(c.getInt(screenIndex), 3);
-        assertTrue(c.getString(intentIndex).contains(testPackage4));
-
-        c.close();
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
new file mode 100644
index 0000000..239e092
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model
+
+import android.content.Context
+import android.content.Intent
+import android.database.sqlite.SQLiteDatabase
+import android.graphics.Point
+import android.os.Process
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherFiles
+import com.android.launcher3.LauncherSettings.Favorites.*
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.model.GridSizeMigrationTaskV2.DbReader
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.provider.LauncherDbUtils
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.*
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Unit tests for [GridSizeMigrationTaskV2]  */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridSizeMigrationTaskV2Test {
+    private lateinit var modelHelper: LauncherModelHelper
+    private lateinit var context: Context
+    private lateinit var db: SQLiteDatabase
+    private lateinit var validPackages: Set<String>
+    private lateinit var idp: InvariantDeviceProfile
+    private val testPackage1 = "com.android.launcher3.validpackage1"
+    private val testPackage2 = "com.android.launcher3.validpackage2"
+    private val testPackage3 = "com.android.launcher3.validpackage3"
+    private val testPackage4 = "com.android.launcher3.validpackage4"
+    private val testPackage5 = "com.android.launcher3.validpackage5"
+    private val testPackage6 = "com.android.launcher3.validpackage6"
+    private val testPackage7 = "com.android.launcher3.validpackage7"
+    private val testPackage8 = "com.android.launcher3.validpackage8"
+    private val testPackage9 = "com.android.launcher3.validpackage9"
+    private val testPackage10 = "com.android.launcher3.validpackage10"
+
+    @Before
+    fun setUp() {
+        modelHelper = LauncherModelHelper()
+        context = modelHelper.sandboxContext
+        db = modelHelper.provider.db
+
+        validPackages = setOf(
+            TEST_PACKAGE,
+            testPackage1,
+            testPackage2,
+            testPackage3,
+            testPackage4,
+            testPackage5,
+            testPackage6,
+            testPackage7,
+            testPackage8,
+            testPackage9,
+            testPackage10
+        )
+
+        idp = InvariantDeviceProfile.INSTANCE[context]
+        val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
+        LauncherDbUtils.dropTable(db, TMP_TABLE)
+        addTableToDb(db, userSerial, false, TMP_TABLE)
+    }
+
+    @After
+    fun tearDown() {
+        modelHelper.destroy()
+    }
+
+    /**
+     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is
+     * not needed anymore
+     */
+    @Test
+    @Throws(Exception::class)
+    fun testMigration() {
+        // Src Hotseat icons
+        modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+        // Src grid icons
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage5, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage6, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage8, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage9, 9, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage10, 10, TMP_CONTENT_URI)
+
+        // Dest hotseat icons
+        modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
+        // Dest grid icons
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7)
+
+        idp.numDatabaseHotseatIcons = 4
+        idp.numColumns = 4
+        idp.numRows = 4
+        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+        val task = GridSizeMigrationTaskV2(
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows)
+        )
+        task.migrate(idp)
+
+        // Check hotseat items
+        var c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(SCREEN, INTENT),
+            "container=$CONTAINER_HOTSEAT",
+            null,
+            SCREEN,
+            null
+        ) ?: throw IllegalStateException()
+
+        assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
+
+        val screenIndex = c.getColumnIndex(SCREEN)
+        var intentIndex = c.getColumnIndex(INTENT)
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex).toLong()).isEqualTo(0)
+        assertThat(c.getString(intentIndex)).contains(testPackage1)
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex).toLong()).isEqualTo(1)
+        assertThat(c.getString(intentIndex)).contains(testPackage2)
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex).toLong()).isEqualTo(2)
+        assertThat(c.getString(intentIndex)).contains(testPackage3)
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex).toLong()).isEqualTo(3)
+        assertThat(c.getString(intentIndex)).contains(testPackage4)
+        c.close()
+
+        // Check workspace items
+        c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(CELLX, CELLY, INTENT),
+            "container=$CONTAINER_DESKTOP",
+            null,
+            null,
+            null
+        ) ?: throw IllegalStateException()
+
+        intentIndex = c.getColumnIndex(INTENT)
+        val cellXIndex = c.getColumnIndex(CELLX)
+        val cellYIndex = c.getColumnIndex(CELLY)
+        val locMap = HashMap<String, Point>()
+        while (c.moveToNext()) {
+            locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+                Point(c.getInt(cellXIndex), c.getInt(cellYIndex))
+        }
+        c.close()
+        assertThat(locMap.size.toLong()).isEqualTo(6)
+        assertThat(locMap[testPackage8]).isEqualTo(Point(0, 2))
+        assertThat(locMap[testPackage6]).isEqualTo(Point(0, 3))
+        assertThat(locMap[testPackage10]).isEqualTo(Point(1, 3))
+        assertThat(locMap[testPackage7]).isEqualTo(Point(2, 2))
+        assertThat(locMap[testPackage5]).isEqualTo(Point(2, 3))
+        assertThat(locMap[testPackage9]).isEqualTo(Point(3, 3))
+    }
+
+    @Test
+    fun migrateToLargerHotseat() {
+        val srcHotseatItems = intArrayOf(
+            modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+            modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+            modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+            modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+        )
+        val numSrcDatabaseHotseatIcons = srcHotseatItems.size
+        idp.numDatabaseHotseatIcons = 6
+        idp.numColumns = 4
+        idp.numRows = 4
+        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+        val task = GridSizeMigrationTaskV2(
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows)
+        )
+        task.migrate(idp)
+
+        // Check hotseat items
+        val c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(SCREEN, INTENT),
+            "container=$CONTAINER_HOTSEAT",
+            null,
+            SCREEN,
+            null
+        ) ?: throw IllegalStateException()
+
+        assertThat(c.count.toLong()).isEqualTo(numSrcDatabaseHotseatIcons.toLong())
+        val screenIndex = c.getColumnIndex(SCREEN)
+        val intentIndex = c.getColumnIndex(INTENT)
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(0)
+        assertThat(c.getString(intentIndex)).contains(testPackage1)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(1)
+        assertThat(c.getString(intentIndex)).contains(testPackage2)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(2)
+        assertThat(c.getString(intentIndex)).contains(testPackage3)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(3)
+        assertThat(c.getString(intentIndex)).contains(testPackage4)
+
+        c.close()
+    }
+
+    @Test
+    fun migrateFromLargerHotseat() {
+        modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
+        modelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI)
+
+        idp.numDatabaseHotseatIcons = 4
+        idp.numColumns = 4
+        idp.numRows = 4
+        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+        val task = GridSizeMigrationTaskV2(
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows)
+        )
+        task.migrate(idp)
+
+        // Check hotseat items
+        val c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(SCREEN, INTENT),
+            "container=$CONTAINER_HOTSEAT",
+            null,
+            SCREEN,
+            null
+        ) ?: throw IllegalStateException()
+
+        assertThat(c.count.toLong()).isEqualTo(idp.numDatabaseHotseatIcons.toLong())
+        val screenIndex = c.getColumnIndex(SCREEN)
+        val intentIndex = c.getColumnIndex(INTENT)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(0)
+        assertThat(c.getString(intentIndex)).contains(testPackage1)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(1)
+        assertThat(c.getString(intentIndex)).contains(testPackage2)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(2)
+        assertThat(c.getString(intentIndex)).contains(testPackage3)
+
+        c.moveToNext()
+        assertThat(c.getInt(screenIndex)).isEqualTo(3)
+        assertThat(c.getString(intentIndex)).contains(testPackage4)
+
+        c.close()
+    }
+
+    /**
+     * Migrating from a smaller grid to a large one should keep the pages
+     * if the column difference is less than 2
+     */
+    @Test
+    @Throws(Exception::class)
+    fun migrateFromSmallerGridSmallDifference() {
+        enableNewMigrationLogic("4,4")
+
+        // Setup src grid
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage1, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage2, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 1, testPackage3, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 2, testPackage4, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 2, DESKTOP, 3, 3, testPackage5, 9, TMP_CONTENT_URI)
+
+        idp.numDatabaseHotseatIcons = 4
+        idp.numColumns = 6
+        idp.numRows = 5
+
+        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+        val task = GridSizeMigrationTaskV2(
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows)
+        )
+        task.migrate(idp)
+
+        // Get workspace items
+        val c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(INTENT, SCREEN),
+            "container=$CONTAINER_DESKTOP",
+            null,
+            null,
+            null
+        ) ?: throw IllegalStateException()
+        val intentIndex = c.getColumnIndex(INTENT)
+        val screenIndex = c.getColumnIndex(SCREEN)
+
+        // Get in which screen the icon is
+        val locMap = HashMap<String, Int>()
+        while (c.moveToNext()) {
+            locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+                c.getInt(screenIndex)
+        }
+        c.close()
+        assertThat(locMap.size).isEqualTo(5)
+        assertThat(locMap[testPackage1]).isEqualTo(0)
+        assertThat(locMap[testPackage2]).isEqualTo(0)
+        assertThat(locMap[testPackage3]).isEqualTo(1)
+        assertThat(locMap[testPackage4]).isEqualTo(1)
+        assertThat(locMap[testPackage5]).isEqualTo(2)
+
+        disableNewMigrationLogic()
+    }
+
+    /**
+     * Migrating from a smaller grid to a large one should reflow the pages
+     * if the column difference is more than 2
+     */
+    @Test
+    @Throws(Exception::class)
+    fun migrateFromSmallerGridBigDifference() {
+        enableNewMigrationLogic("2,2")
+
+        // Setup src grid
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+
+        idp.numDatabaseHotseatIcons = 4
+        idp.numColumns = 5
+        idp.numRows = 5
+        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+        val task = GridSizeMigrationTaskV2(
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows)
+        )
+        task.migrate(idp)
+
+        // Get workspace items
+        val c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(INTENT, SCREEN),
+            "container=$CONTAINER_DESKTOP",
+            null,
+            null,
+            null
+        ) ?: throw IllegalStateException()
+
+        val intentIndex = c.getColumnIndex(INTENT)
+        val screenIndex = c.getColumnIndex(SCREEN)
+
+        // Get in which screen the icon is
+        val locMap = HashMap<String, Int>()
+        while (c.moveToNext()) {
+            locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+                c.getInt(screenIndex)
+        }
+        c.close()
+
+        // All icons fit the first screen
+        assertThat(locMap.size).isEqualTo(5)
+        assertThat(locMap[testPackage1]).isEqualTo(0)
+        assertThat(locMap[testPackage2]).isEqualTo(0)
+        assertThat(locMap[testPackage3]).isEqualTo(0)
+        assertThat(locMap[testPackage4]).isEqualTo(0)
+        assertThat(locMap[testPackage5]).isEqualTo(0)
+        disableNewMigrationLogic()
+    }
+
+    /**
+     * Migrating from a larger grid to a smaller, we reflow from page 0
+     */
+    @Test
+    @Throws(Exception::class)
+    fun migrateFromLargerGrid() {
+        enableNewMigrationLogic("5,5")
+
+        // Setup src grid
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+
+        idp.numDatabaseHotseatIcons = 4
+        idp.numColumns = 4
+        idp.numRows = 4
+        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
+        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
+        val task = GridSizeMigrationTaskV2(
+            context,
+            db,
+            srcReader,
+            destReader,
+            idp.numDatabaseHotseatIcons,
+            Point(idp.numColumns, idp.numRows)
+        )
+        task.migrate(idp)
+
+        // Get workspace items
+        val c = context.contentResolver.query(
+            CONTENT_URI,
+            arrayOf(INTENT, SCREEN),
+            "container=$CONTAINER_DESKTOP",
+            null,
+            null,
+            null
+        ) ?: throw IllegalStateException()
+        val intentIndex = c.getColumnIndex(INTENT)
+        val screenIndex = c.getColumnIndex(SCREEN)
+
+        // Get in which screen the icon is
+        val locMap = HashMap<String, Int>()
+        while (c.moveToNext()) {
+            locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+                c.getInt(screenIndex)
+        }
+        c.close()
+
+        // All icons fit the first screen
+        assertThat(locMap.size).isEqualTo(5)
+        assertThat(locMap[testPackage1]).isEqualTo(0)
+        assertThat(locMap[testPackage2]).isEqualTo(0)
+        assertThat(locMap[testPackage3]).isEqualTo(0)
+        assertThat(locMap[testPackage4]).isEqualTo(0)
+        assertThat(locMap[testPackage5]).isEqualTo(0)
+
+        disableNewMigrationLogic()
+    }
+
+    private fun enableNewMigrationLogic(srcGridSize: String) {
+        context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+            .edit()
+            .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true)
+            .commit()
+        context.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
+            .edit()
+            .putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize)
+            .commit()
+        FeatureFlags.initialize(context)
+    }
+
+    private fun disableNewMigrationLogic() {
+        context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+            .edit()
+            .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, false)
+            .commit()
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 79a4673..5abdcd3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.rule.CrashDetector;
 import android.system.OsConstants;
 import android.util.Log;
 
@@ -225,6 +226,7 @@
     @Rule
     public TestRule mOrderSensitiveRules = RuleChain
             .outerRule(new TestStabilityRule())
+            .around(new CrashDetector("com.android.systemui"))
             .around(mActivityMonitor)
             .around(getRulesInsideActivityMonitor());
 
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
new file mode 100644
index 0000000..e66810c
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.ui.workspace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.icons.ThemedIconDrawable;
+import com.android.launcher3.tapl.HomeAllApps;
+import com.android.launcher3.tapl.HomeAppIcon;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+
+import org.junit.Test;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * Tests for theme icon support in Launcher
+ *
+ * Note running these tests will clear the workspace on the device.
+ */
+@LargeTest
+public class ThemeIconsTest extends AbstractLauncherUiTest {
+
+    private static final String APP_NAME = "ThemeIconTestActivity";
+
+    @Test
+    public void testIconWithoutTheme() throws Exception {
+        setThemeEnabled(false);
+        TaplTestsLauncher3.initialize(this);
+
+        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+
+        try {
+            HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
+            executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+            icon.dragToWorkspace(false, false);
+            executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), false));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    @Test
+    public void testIconWithTheme() throws Exception {
+        setThemeEnabled(true);
+        TaplTestsLauncher3.initialize(this);
+
+        HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+
+        try {
+            HomeAppIcon icon = allApps.getAppIcon(APP_NAME);
+            executeOnLauncher(l -> verifyIconTheme(l.getAppsView(), false));
+            icon.dragToWorkspace(false, false);
+            executeOnLauncher(l -> verifyIconTheme(l.getWorkspace(), true));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    private void verifyIconTheme(ViewGroup parent, boolean isThemed) {
+        // Find the app icon
+        Queue<View> viewQueue = new ArrayDeque<>();
+        viewQueue.add(parent);
+        BubbleTextView icon = null;
+        while (!viewQueue.isEmpty()) {
+            View view = viewQueue.poll();
+            if (view instanceof ViewGroup) {
+                parent = (ViewGroup) view;
+                for (int i = parent.getChildCount() - 1; i >= 0; i--) {
+                    viewQueue.add(parent.getChildAt(i));
+                }
+            } else if (view instanceof BubbleTextView) {
+                BubbleTextView btv = (BubbleTextView) view;
+                if (APP_NAME.equals(btv.getText())) {
+                    icon = btv;
+                    break;
+                }
+            }
+        }
+
+        assertNotNull(icon.getIcon());
+        assertEquals(isThemed, icon.getIcon() instanceof ThemedIconDrawable);
+    }
+
+    private void setThemeEnabled(boolean isEnabled) throws Exception {
+        Uri uri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(mTargetPackage + ".grid_control")
+                .appendPath("set_icon_themed")
+                .build();
+        ContentValues values = new ContentValues();
+        values.put("boolean_value", isEnabled);
+        try (ContentProviderClient client = mTargetContext.getContentResolver()
+                .acquireContentProviderClient(uri)) {
+            int result = client.update(uri, values, null);
+            assertTrue(result > 0);
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt b/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt
index c4a8db6..6099987 100644
--- a/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt
+++ b/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt
@@ -1,5 +1,6 @@
 package com.android.launcher3.util
 
+import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -14,8 +15,8 @@
     private val received = mutableListOf<Float>()
 
     private val factory =
-        object : MultiScalePropertyFactory<Int?>("Test") {
-            override fun apply(obj: Int?, value: Float) {
+        object : MultiScalePropertyFactory<View?>("Test") {
+            override fun apply(obj: View?, value: Float) {
                 received.add(value)
             }
         }