Merge "Add feature flag to force taskbar to be persistent." into tm-qpr-dev
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 0ac7ee5..2742bfc 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"ሁሉም ዝግጁ!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ወደ መነሻ ለመሄድ በጣት ወደ ላይ ማንሸራተት"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"ወደ መነሻ ማያ ገጽዎ ለመሄድ የመነሻ አዝራሩን መታ ያድርጉ"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"የእርስዎን <xliff:g id="DEVICE">%1$s</xliff:g> መጠቀም ለመጀመር ዝግጁ ነዎት"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"መሣሪያ"</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>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"ይቅር"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ዝለል"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"ማያ ገጹን አዙር"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"የተግባር አሞሌ ትምህርት"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"የተግባር አሞሌ ትምህርት ይታያል"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"የተግባር አሞሌ ትምህርት ተዘግቷል"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"መተግበሪያዎችን ለመቀየር የተግባር አሞሌውን ይጠቀሙ"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"የቅርብ ጊዜዎቹ"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"ማሳወቂያዎች"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ፈጣን ቅንብሮች"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"የተግባር አሞሌ"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"የአሰሳ አሞሌ"</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-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index fe76453..041fe24 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"اكتملت عملية الإعداد"</string>
     <string name="allset_hint" msgid="2384632994739392447">"مرِّر سريعًا للأعلى للانتقال إلى الشاشة الرئيسية."</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"انقر على زر الشاشة الرئيسية للانتقال إلى الشاشة الرئيسية."</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"يمكنك الآن بدء استخدام \"<xliff:g id="DEVICE">%1$s</xliff:g>\"."</string>
+    <string name="default_device_name" msgid="6660656727127422487">"الجهاز"</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>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"إلغاء"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"التخطي"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"تدوير الشاشة"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"التعريف بشريط التطبيقات"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"ظهرت لوحة تعليم استخدام شريط المهام."</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"تم إغلاق لوحة تعليم استخدام شريط المهام."</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"يمكنك استخدام شريط المهام للتبديل بين التطبيقات."</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"الأحدث"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"الإشعارات"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"إعدادات سريعة"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"شريط التطبيقات"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"شريط التنقل"</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-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 8fa8533..8028677 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"Dena prest!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Pasatu hatza gora hasierako pantailara joateko"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Hasierako pantailara joateko, sakatu Hasiera botoia"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"Prest zaude <xliff:g id="DEVICE">%1$s</xliff:g> erabiltzen hasteko"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"gailua"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sisteman nabigatzeko ezarpenak"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Partekatu"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</string>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Utzi"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Saltatu"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"Biratu pantaila"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Zereginen barra erabiltzeko argibideak"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"Agertu egin da zereginen barraren tutoriala"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"Itxi egin da zereginen barraren tutoriala"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Erabili zereginen barra aplikazioz aldatzeko"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Azkenak"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"Jakinarazpenak"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ezarpen bizkorrak"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"Zereginen barra"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Nabigazio-barra"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Eraman gora, ezkerretara"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string>
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index c52f39c..0c0ec88 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"બધું સેટ થઈ ગયું!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"હોમપેજ પર જવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"તમારી હોમ સ્ક્રીન પર જવા માટે હોમ બટન ટૅપ કરો"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"તમે તમારા <xliff:g id="DEVICE">%1$s</xliff:g>નો ઉપયોગ કરવાનું શરૂ કરવા માટે તૈયાર છો"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"ડિવાઇસ"</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>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"રદ કરો"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"છોડો"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"સ્ક્રીન ફેરવો"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"ટાસ્કબાર વિશે શિક્ષણ"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ દેખાય છે"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ બંધ થઈ છે"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ઍપ સ્વિચ કરવા માટે, ટાસ્કબારનો ઉપયોગ કરો"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"તાજેતરના"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"નોટિફિકેશન"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"ઝડપી સેટિંગ"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"ટાસ્કબાર"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"નૅવિગેશન બાર"</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-my/strings.xml b/quickstep/res/values-my/strings.xml
index 825a263..8fcb464 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"အားလုံး အဆင်သင့်ပါ။"</string>
     <string name="allset_hint" msgid="2384632994739392447">"ပင်မစာမျက်နှာသို့သွားရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"ပင်မစာမျက်နှာသို့ သွားရန် ပင်မခလုတ်ကို တို့ပါ"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> ကို စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"စက်"</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>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"မလုပ်တော့"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"ကျော်ရန်"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"ဖန်သားပြင်လှည့်ရန်"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"လုပ်ဆောင်စရာဘား ပညာပေး"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"ပညာရေး လုပ်ဆောင်စရာဘား ပြထားသည်"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"ပညာရေး လုပ်ဆောင်စရာဘား ပိတ်ထားသည်"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"အက်ပ်များပြောင်းရန် လုပ်ဆောင်စရာဘားကို သုံးပါ"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"လတ်တလောများ"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"အကြောင်းကြားချက်"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"အမြန်ဆက်တင်များ"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"လုပ်ဆောင်စရာဘား"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"လမ်းညွှန်ဘား"</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-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 4b2bc9c..a0f8221 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"Tudo pronto!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Deslize para cima para acessar a tela inicial"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Toque no botão home para ir para a tela inicial"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"Você já pode começar a usar seu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Configurações de navegação do sistema"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Cancelar"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Pular"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"Girar a tela"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informações sobre a barra de tarefas"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"As dicas sobre a barra de tarefas foram abertas"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"As dicas sobre a barra de tarefas foram fechadas"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Use a barra de tarefas para alternar entre apps"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Recentes"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"Notificações"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. rápidas"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para cima/para a esquerda"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 09263ea..8283a04 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"Tayari!"</string>
     <string name="allset_hint" msgid="2384632994739392447">"Telezesha kidole juu ili uende kwenye skrini ya kwanza"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Gusa kitufe cha ukurasa wa mwanzo ili uende kwenye skrini ya kwanza"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"Uko tayari kuanza kutumia <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"kifaa"</string>
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Mipangilio ya usogezaji kwenye mfumo"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Shiriki"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</string>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Ghairi"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ruka"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"Zungusha skrini"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Elimu ya Upauzana"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"Paneli ya elimu kwenye upau wa shughuli inaonyeshwa"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"Paneli ya elimu kwenye upau wa shughuli imefungwa"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Tumia upau wa shughuli kubadilisha programu"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Vilivyotumika majuzi"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"Arifa"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Mipangilio ya Haraka"</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"Upauzana"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Sehemu ya viungo muhimu"</string>
     <string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sogeza juu/kushoto"</string>
     <string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 76422a4..d85ccca 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -78,10 +78,8 @@
     <string name="allset_title" msgid="5021126669778966707">"Готово."</string>
     <string name="allset_hint" msgid="2384632994739392447">"Щоб перейти на головний екран, проведіть пальцем угору"</string>
     <string name="allset_button_hint" msgid="2395219947744706291">"Натисніть кнопку головного екрана, щоб відкрити його"</string>
-    <!-- no translation found for allset_description_generic (5385500062202019855) -->
-    <skip />
-    <!-- no translation found for default_device_name (6660656727127422487) -->
-    <skip />
+    <string name="allset_description_generic" msgid="5385500062202019855">"Тепер ви можете користуватися цим пристроєм: <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+    <string name="default_device_name" msgid="6660656727127422487">"пристрій"</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>
@@ -94,8 +92,7 @@
     <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Скасувати"</string>
     <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Пропустити"</string>
     <string name="accessibility_rotate_button" msgid="4771825231336502943">"Обернути екран"</string>
-    <!-- no translation found for taskbar_edu_a11y_title (5417986057866415355) -->
-    <skip />
+    <string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Панель завдань Education"</string>
     <string name="taskbar_edu_opened" msgid="3950252793551919129">"Панель завдань Education відкрито"</string>
     <string name="taskbar_edu_closed" msgid="126643734478892862">"Панель завдань Education закрито"</string>
     <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Переходьте між додатками за допомогою панелі завдань"</string>
@@ -112,10 +109,8 @@
     <string name="taskbar_button_recents" msgid="7273376136216613134">"Нещодавні"</string>
     <string name="taskbar_button_notifications" msgid="7471740351507357318">"Сповіщення"</string>
     <string name="taskbar_button_quick_settings" msgid="227662894293189391">"Швидкі налаштув."</string>
-    <!-- no translation found for taskbar_a11y_title (6432169809852243110) -->
-    <skip />
-    <!-- no translation found for taskbar_phone_a11y_title (4933360237131229395) -->
-    <skip />
+    <string name="taskbar_a11y_title" msgid="6432169809852243110">"Панель завдань"</string>
+    <string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навігації"</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/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherLifecycleListener.java
similarity index 78%
rename from quickstep/src/com/android/launcher3/LauncherInitListener.java
rename to quickstep/src/com/android/launcher3/LauncherLifecycleListener.java
index 28bd701..82c646d 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherLifecycleListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,19 +16,23 @@
 package com.android.launcher3;
 
 import android.animation.AnimatorSet;
+import android.annotation.Nullable;
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.CancellationSignal;
 import android.view.RemoteAnimationTarget;
 
 import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.ActivityLifecycleListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
 
 import java.util.function.BiPredicate;
 
+/**
+ * {@link ActivityLifecycleListener} for the in-launcher recents.
+ */
 @TargetApi(Build.VERSION_CODES.P)
-public class LauncherInitListener extends ActivityInitListener<Launcher> {
+public class LauncherLifecycleListener extends ActivityLifecycleListener<Launcher> {
 
     private RemoteAnimationProvider mRemoteAnimationProvider;
 
@@ -36,13 +40,16 @@
      * @param onInitListener a callback made when the activity is initialized. The callback should
      *                       return true to continue receiving callbacks (ie. for if the activity is
      *                       recreated).
+     * @param onDestroyListener a callback made when the activity is destroyed.
      */
-    public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
-        super(onInitListener, Launcher.ACTIVITY_TRACKER);
+    public LauncherLifecycleListener(
+            @Nullable BiPredicate<Launcher, Boolean> onInitListener,
+            @Nullable Runnable onDestroyListener) {
+        super(onInitListener, onDestroyListener, Launcher.ACTIVITY_TRACKER);
     }
 
     @Override
-    public boolean handleInit(Launcher launcher, boolean alreadyOnHome) {
+    public boolean handleActivityReady(Launcher launcher, boolean alreadyOnHome) {
         if (mRemoteAnimationProvider != null) {
             QuickstepTransitionManager appTransitionManager =
                     ((QuickstepLauncher) launcher).getAppTransitionManager();
@@ -68,7 +75,7 @@
             }, cancellationSignal);
         }
         launcher.deferOverlayCallbacksUntilNextResumeOrStop();
-        return super.handleInit(launcher, alreadyOnHome);
+        return super.handleActivityReady(launcher, alreadyOnHome);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 80bdb6f..ba412c9 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -193,7 +193,7 @@
             icon.setEnabled(false);
             icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
             icon.verifyHighRes();
-            CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1);
+            CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1, -1);
             mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 3d8ffc4..e41841a 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -50,6 +50,7 @@
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
 import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -113,7 +114,7 @@
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.ActivityLifecycleListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.InputConsumerProxy;
 import com.android.quickstep.util.InputProxyHandlerFactory;
@@ -159,7 +160,7 @@
 
     protected final BaseActivityInterface<S, T> mActivityInterface;
     protected final InputConsumerProxy mInputConsumerProxy;
-    protected final ActivityInitListener mActivityInitListener;
+    protected final ActivityLifecycleListener mActivityInitListener;
     // Callbacks to be made once the recents animation starts
     private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
     private final OnScrollChangedListener mOnRecentsScrollListener = this::onRecentsViewScroll;
@@ -329,7 +330,8 @@
             InputConsumerController inputConsumer) {
         super(context, deviceState, gestureState);
         mActivityInterface = gestureState.getActivityInterface();
-        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
+        mActivityInitListener = mActivityInterface.createActivityLifecycleListener(
+                this::onActivityInit, this::onActivityDestroy);
         mInputConsumerProxy =
                 new InputConsumerProxy(context, /* rotationSupplier = */ () -> {
                     if (mRecentsView == null) {
@@ -520,6 +522,11 @@
         return true;
     }
 
+    private void onActivityDestroy() {
+        ActiveGestureLog.INSTANCE.addLog("Launcher activity destroyed", LAUNCHER_DESTROYED);
+        onGestureCancelled();
+    }
+
     /**
      * Return true if the window should be translated horizontally if the recents view scrolls
      */
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 274b686..5523518 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -60,7 +60,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.ActivityLifecycleListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -123,8 +123,17 @@
     public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
             boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
 
-    public abstract ActivityInitListener createActivityInitListener(
-            Predicate<Boolean> onInitListener);
+    /**
+     * Creates a activity listener for activity initialized and/or destroyed. One or both of these
+     * listeners must be provided.
+     *
+     * @param onInitListener a callback made when the activity is initialized. The callback should
+     *                       return true to continue receiving callbacks (ie. for if the activity is
+     *                       recreated).
+     * @param onDestroyListener a callback made when the activity is destroyed.
+     */
+    public abstract ActivityLifecycleListener createActivityLifecycleListener(
+            @Nullable Predicate<Boolean> onInitListener, @Nullable Runnable onDestroyListener);
 
     /**
      * Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index ae9fb0b..a658566 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -36,7 +36,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.fallback.RecentsState;
-import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.ActivityLifecycleListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.views.RecentsView;
 
@@ -88,10 +88,12 @@
     }
 
     @Override
-    public ActivityInitListener createActivityInitListener(
-            Predicate<Boolean> onInitListener) {
-        return new ActivityInitListener<>((activity, alreadyOnHome) ->
-                onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
+    public ActivityLifecycleListener createActivityLifecycleListener(
+            @Nullable Predicate<Boolean> onInitListener, @Nullable Runnable onDestroyListener) {
+        return new ActivityLifecycleListener<>(
+                (activity, alreadyOnHome) -> onInitListener.test(alreadyOnHome),
+                onDestroyListener,
+                RecentsActivity.ACTIVITY_TRACKER);
     }
 
     @Nullable
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 9ff9416..cb54d2e 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -37,7 +37,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherLifecycleListener;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
@@ -49,7 +49,7 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.GestureState.GestureEndTarget;
-import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.ActivityLifecycleListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
@@ -136,9 +136,10 @@
     }
 
     @Override
-    public ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
-        return new LauncherInitListener((activity, alreadyOnHome) ->
-                onInitListener.test(alreadyOnHome));
+    public ActivityLifecycleListener createActivityLifecycleListener(
+            @Nullable Predicate<Boolean> onInitListener, @Nullable Runnable onDestroyListener) {
+        return new LauncherLifecycleListener((activity, alreadyOnHome) ->
+                onInitListener.test(alreadyOnHome), onDestroyListener);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 60065fb..0fdd8b5 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -37,7 +37,7 @@
         ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
         CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
         CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
-        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
+        FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
 
         /**
          * These GestureEvents are specifically associated to state flags that get set in
@@ -162,6 +162,13 @@
                                         + "before/without setting end target to new task",
                                 writer);
                         break;
+                    case LAUNCHER_DESTROYED:
+                        errorDetected |= printErrorIfTrue(
+                                true,
+                                prefix,
+                                /* errorMessage= */ "Launcher destroyed mid-gesture",
+                                writer);
+                        break;
                     case STATE_GESTURE_COMPLETED:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.MOTION_UP),
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
deleted file mode 100644
index aeec36f..0000000
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.util.ActivityTracker;
-import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
-
-import java.util.function.BiPredicate;
-
-public class ActivityInitListener<T extends BaseActivity> implements
-        SchedulerCallback<T> {
-
-    private BiPredicate<T, Boolean> mOnInitListener;
-    private final ActivityTracker<T> mActivityTracker;
-
-    private boolean mIsRegistered = false;
-
-    /**
-     * @param onInitListener a callback made when the activity is initialized. The callback should
-     *                       return true to continue receiving callbacks (ie. for if the activity is
-     *                       recreated).
-     */
-    public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
-            ActivityTracker<T> tracker) {
-        mOnInitListener = onInitListener;
-        mActivityTracker = tracker;
-    }
-
-    @Override
-    public final boolean init(T activity, boolean alreadyOnHome) {
-        if (!mIsRegistered) {
-            // Don't receive any more updates
-            return false;
-        }
-        return handleInit(activity, alreadyOnHome);
-    }
-
-    protected boolean handleInit(T activity, boolean alreadyOnHome) {
-        return mOnInitListener.test(activity, alreadyOnHome);
-    }
-
-    /**
-     * Registers the activity-created listener. If the activity is already created, then the
-     * callback provided in the constructor will be called synchronously.
-     */
-    public void register() {
-        mIsRegistered = true;
-        mActivityTracker.registerCallback(this);
-    }
-
-    /**
-     * After calling this, we won't {@link #init} even when the activity is ready.
-     */
-    public void unregister() {
-        mActivityTracker.unregisterCallback(this);
-        mIsRegistered = false;
-        mOnInitListener = null;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/ActivityLifecycleListener.java b/quickstep/src/com/android/quickstep/util/ActivityLifecycleListener.java
new file mode 100644
index 0000000..1edf188
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ActivityLifecycleListener.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
+
+import java.util.function.BiPredicate;
+
+/**
+ * Listener for activity initialized and/or destroyed.
+ */
+public class ActivityLifecycleListener<T extends BaseActivity> implements
+        SchedulerCallback<T> {
+
+    private static final String TAG = "ActivityLifecycleListener";
+
+    @Nullable private final BiPredicate<T, Boolean> mOnInitListener;
+    @Nullable private final Runnable mOnDestroyListener;
+    private final ActivityTracker<T> mActivityTracker;
+
+    private boolean mIsRegistered = false;
+
+    /**
+     * One or both of {@code onInitListener} and {@code onInitListener} must be provided, otherwise
+     * the created instance will effectively be a no-op.
+     *
+     * @param onInitListener a callback made when the activity is initialized. The callback should
+     *                       return true to continue receiving callbacks (ie. for if the activity is
+     *                       recreated).
+     * @param onDestroyListener a callback made when the activity is destroyed.
+     */
+    public ActivityLifecycleListener(
+            @Nullable BiPredicate<T, Boolean> onInitListener,
+            @Nullable Runnable onDestroyListener,
+            ActivityTracker<T> tracker) {
+        if (onInitListener == null && onDestroyListener == null) {
+            throw new IllegalArgumentException("Both listeners cannot be null");
+        }
+        mOnInitListener = onInitListener;
+        mOnDestroyListener = onDestroyListener;
+        mActivityTracker = tracker;
+    }
+
+    @Override
+    public final boolean onActivityReady(T activity, boolean alreadyOnHome) {
+        if (!mIsRegistered) {
+            // Don't receive any more updates
+            return false;
+        }
+        return handleActivityReady(activity, alreadyOnHome);
+    }
+
+    protected boolean handleActivityReady(T activity, boolean alreadyOnHome) {
+        if (mOnInitListener == null) {
+            Log.e(TAG, "Cannot handle init: init listener is null", new Exception());
+            return false;
+        }
+        return mOnInitListener.test(activity, alreadyOnHome);
+    }
+
+    @Override
+    public void onActivityDestroyed() {
+        if (mOnDestroyListener == null) {
+            Log.e(TAG, "Cannot clean up: destroy listener is null", new Exception());
+            return;
+        }
+        mOnDestroyListener.run();
+    }
+
+    /**
+     * Registers the activity-created listener. If the activity is already created, then the
+     * callback provided in the constructor will be called synchronously.
+     */
+    public void register() {
+        mIsRegistered = true;
+        mActivityTracker.registerCallback(this, getType());
+    }
+
+    /**
+     * After calling this, we won't call {@link #onActivityReady} even when the activity is ready.
+     */
+    public void unregister() {
+        mActivityTracker.unregisterCallback(this, getType());
+        mIsRegistered = false;
+    }
+
+    private int getType() {
+        return mOnInitListener != null && mOnDestroyListener != null
+                ? ActivityTracker.TYPE_BOTH
+                : (mOnInitListener != null
+                        ? ActivityTracker.TYPE_INIT
+                        : ActivityTracker.TYPE_DESTROY);
+    }
+}
diff --git a/res/color-night-v31/all_apps_tab_text.xml b/res/color-night-v31/all_apps_tab_text.xml
index 83237b4..54b95ae 100644
--- a/res/color-night-v31/all_apps_tab_text.xml
+++ b/res/color-night-v31/all_apps_tab_text.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/system_neutral1_50" android:state_selected="true"/>
-    <item android:color="@android:color/system_neutral2_700"/>
+    <item android:color="@android:color/system_neutral2_700" android:state_selected="true"/>
+    <item android:color="@android:color/system_accent2_100"/>
 </selector>
\ No newline at end of file
diff --git a/res/color-night-v31/all_apps_tabs_background.xml b/res/color-night-v31/all_apps_tabs_background.xml
new file mode 100644
index 0000000..9213274
--- /dev/null
+++ b/res/color-night-v31/all_apps_tabs_background.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_800"/>
+</selector>
\ No newline at end of file
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 5abe3d3..ee3e278 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -423,14 +423,15 @@
         float textSize = Utilities.pxToSp(getTextSize());
 
         int availableWidth = getMeasuredWidth();
-        while (textSize > minSize) {
-            if (isTextTruncated(availableWidth)) {
-                textSize -= step;
+        while (isTextTruncated(availableWidth)) {
+            textSize -= step;
+            if (textSize < minSize) {
+                textSize = minSize;
                 setTextSize(textSize);
-            } else {
-                return textSize;
+                break;
             }
+            setTextSize(textSize);
         }
-        return minSize;
+        return textSize;
     }
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9c5e906..fdc52b5 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -267,7 +267,7 @@
         mDragCell[0] = mDragCell[1] = -1;
         mDragCellSpan[0] = mDragCellSpan[1] = -1;
         for (int i = 0; i < mDragOutlines.length; i++) {
-            mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0);
+            mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0, -1);
         }
         mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 27c9e98..fd670c1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -582,7 +582,7 @@
         }
 
         int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
-        CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
+        CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1, FIRST_SCREEN_ID);
         lp.canReorder = false;
         if (!firstPage.addViewToCellLayout(
                 mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) {
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 0b3a62f..91e12fa 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -114,7 +114,7 @@
         ViewGroup.LayoutParams genericLp = child.getLayoutParams();
         CellLayoutLayoutParams lp;
         if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
-            lp = new CellLayoutLayoutParams(x, y, spanX, spanY);
+            lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId);
         } else {
             lp = (CellLayoutLayoutParams) genericLp;
             lp.cellX = x;
diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
index b14ae8d..abd4682 100644
--- a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -28,6 +28,9 @@
  * Represents the logic of where a view is in a CellLayout and its size
  */
 public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams {
+
+    public int screenId = -1;
+
     /**
      * Horizontal location of the item in the grid.
      */
@@ -106,14 +109,20 @@
         this.cellY = source.cellY;
         this.cellHSpan = source.cellHSpan;
         this.cellVSpan = source.cellVSpan;
+        this.screenId = source.screenId;
+        this.tmpCellX = source.tmpCellX;
+        this.tmpCellY = source.tmpCellY;
+        this.useTmpCoords = source.useTmpCoords;
     }
 
-    public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
+    public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan,
+            int screenId) {
         super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT);
         this.cellX = cellX;
         this.cellY = cellY;
         this.cellHSpan = cellHSpan;
         this.cellVSpan = cellVSpan;
+        this.screenId = screenId;
     }
 
     /**
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 5a49f4a..a610548 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_START;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
 
 import android.annotation.TargetApi;
 import android.app.ActivityOptions;
@@ -63,10 +64,11 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.views.AbstractSlideInView;
 import com.android.launcher3.views.BaseDragLayer;
@@ -80,6 +82,7 @@
 import com.android.launcher3.widget.WidgetCellPreview;
 import com.android.launcher3.widget.WidgetImageView;
 import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.WidgetSections;
 
 import java.util.function.Supplier;
 
@@ -142,7 +145,7 @@
         mAccessibilityManager =
                 getApplicationContext().getSystemService(AccessibilityManager.class);
 
-        PackageUserKey targetApp = null;
+        final PackageItemInfo targetApp;
         switch (mRequest.getRequestType()) {
             case PinItemRequest.REQUEST_TYPE_SHORTCUT:
                 targetApp = setupShortcut();
@@ -150,6 +153,9 @@
             case PinItemRequest.REQUEST_TYPE_APPWIDGET:
                 targetApp = setupWidget();
                 break;
+            default:
+                targetApp = null;
+                break;
         }
         if (targetApp == null) {
             // TODO: show error toast?
@@ -157,7 +163,7 @@
             return;
         }
         ApplicationInfo info = new PackageManagerHelper(this)
-                .getApplicationInfo(targetApp.mPackageName, targetApp.mUser, 0);
+                .getApplicationInfo(targetApp.packageName, targetApp.user, 0);
         if (info == null) {
             finish();
             return;
@@ -177,7 +183,10 @@
         // Set the label synchronously instead of via IconCache as this is the first thing
         // user sees
         TextView widgetAppName = findViewById(R.id.widget_appName);
-        widgetAppName.setText(info.loadLabel(getPackageManager()));
+        WidgetSections.WidgetSection section = targetApp.widgetCategory == NO_CATEGORY ? null
+                : WidgetSections.getWidgetSections(this).get(targetApp.widgetCategory);
+        widgetAppName.setText(section == null ? info.loadLabel(getPackageManager())
+                : getString(section.mSectionTitle));
 
         mSlideInView = findViewById(R.id.add_item_bottom_sheet);
         mSlideInView.addOnCloseListener(this);
@@ -266,19 +275,18 @@
         }
     }
 
-    private PackageUserKey setupShortcut() {
+    private PackageItemInfo setupShortcut() {
         PinShortcutRequestActivityInfo shortcutInfo =
                 new PinShortcutRequestActivityInfo(mRequest, this);
         mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
         applyWidgetItemAsync(
                 () -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
-        return new PackageUserKey(
-                mRequest.getShortcutInfo().getPackage(),
+        return new PackageItemInfo(mRequest.getShortcutInfo().getPackage(),
                 mRequest.getShortcutInfo().getUserHandle());
     }
 
-    private PackageUserKey setupWidget() {
-        LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
+    private PackageItemInfo setupWidget() {
+        final LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
                 .fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this));
         if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) {
             // Cannot add widget
@@ -297,7 +305,8 @@
         mWidgetCell.getWidgetView().setTag(pendingInfo);
 
         applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
-        return new PackageUserKey(widgetInfo.provider.getPackageName(), widgetInfo.getUser());
+        return WidgetsModel.newPendingItemInfo(this, widgetInfo.getComponent(),
+                widgetInfo.getUser());
     }
 
     private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 981e3a6..6d127b3 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public boolean init(Launcher launcher, boolean alreadyOnHome) {
+    public boolean onActivityReady(Launcher launcher, boolean alreadyOnHome) {
         AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
         launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */);
         launcher.getDragLayer().setOnDragListener(this);
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index af43ae8..fb924c1 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -72,8 +72,8 @@
     }
 
     @Override
-    public boolean init(Launcher launcher, boolean alreadyOnHome) {
-        super.init(launcher, alreadyOnHome);
+    public boolean onActivityReady(Launcher launcher, boolean alreadyOnHome) {
+        super.onActivityReady(launcher, alreadyOnHome);
         if (!alreadyOnHome) {
             launcher.useFadeOutAnimationForLauncherStart(mCancelSignal);
         }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index b87ab17..141388f 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -223,7 +223,7 @@
         CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams();
         if (lp == null) {
             textView.setLayoutParams(new CellLayoutLayoutParams(
-                    item.cellX, item.cellY, item.spanX, item.spanY));
+                    item.cellX, item.cellY, item.spanX, item.spanY, item.screenId));
         } else {
             lp.cellX = item.cellX;
             lp.cellY = item.cellY;
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 2361907..772913d 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -534,7 +534,7 @@
             View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen,
                     false);
             CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, firstScreen.getCountX(),
-                    1);
+                    1, FIRST_SCREEN_ID);
             lp.canReorder = false;
             firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
         }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 0e42d58..24d8c9d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -779,7 +779,8 @@
             FAIL(4),
             COLD_USERWAITING(5),
             ATOMIC(6),
-            CONTROLLED(7);
+            CONTROLLED(7),
+            CACHED(8);
             private final int mId;
 
             LatencyType(int id) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index d63408b..eded5ea 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -66,7 +66,7 @@
 public class GridSizeMigrationUtil {
 
     private static final String TAG = "GridSizeMigrationUtil";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private GridSizeMigrationUtil() {
         // Util class should not be instantiated
@@ -188,27 +188,54 @@
             @NonNull final DeviceGridState srcDeviceState,
             @NonNull final DeviceGridState destDeviceState) {
 
-        final List<DbEntry> hotseatItems = destReader.loadHotseatEntries();
-        final List<DbEntry> workspaceItems = destReader.loadAllWorkspaceEntries();
-        final List<DbEntry> hotseatDiff =
-                calcDiff(srcReader.loadHotseatEntries(), hotseatItems);
-        final List<DbEntry> workspaceDiff =
-                calcDiff(srcReader.loadAllWorkspaceEntries(), workspaceItems);
+        final List<DbEntry> srcHotseatItems = srcReader.loadHotseatEntries();
+        final List<DbEntry> srcWorkspaceItems = srcReader.loadAllWorkspaceEntries();
+        final List<DbEntry> dstHotseatItems = destReader.loadHotseatEntries();
+        final List<DbEntry> dstWorkspaceItems = destReader.loadAllWorkspaceEntries();
+        final List<DbEntry> hotseatToBeAdded = new ArrayList<>(1);
+        final List<DbEntry> workspaceToBeAdded = new ArrayList<>(1);
+        final IntArray toBeRemoved = new IntArray();
+
+        calcDiff(srcHotseatItems, dstHotseatItems, hotseatToBeAdded, toBeRemoved);
+        calcDiff(srcWorkspaceItems, dstWorkspaceItems, workspaceToBeAdded, toBeRemoved);
 
         final int trgX = targetSize.x;
         final int trgY = targetSize.y;
 
-        if (hotseatDiff.isEmpty() && workspaceDiff.isEmpty()) {
+        if (DEBUG) {
+            Log.d(TAG, "Start migration:"
+                    + "\n Source Device:"
+                    + srcWorkspaceItems.stream().map(DbEntry::toString).collect(
+                    Collectors.joining(",\n", "[", "]"))
+                    + "\n Target Device:"
+                    + dstWorkspaceItems.stream().map(DbEntry::toString).collect(
+                    Collectors.joining(",\n", "[", "]"))
+                    + "\n Removing Items:"
+                    + dstWorkspaceItems.stream().filter(entry ->
+                            toBeRemoved.contains(entry.id)).map(DbEntry::toString).collect(
+                    Collectors.joining(",\n", "[", "]"))
+                    + "\n Adding Workspace Items:"
+                    + workspaceToBeAdded.stream().map(DbEntry::toString).collect(
+                    Collectors.joining(",\n", "[", "]"))
+                    + "\n Adding Hotseat Items:"
+                    + hotseatToBeAdded.stream().map(DbEntry::toString).collect(
+                    Collectors.joining(",\n", "[", "]"))
+            );
+        }
+        if (!toBeRemoved.isEmpty()) {
+            removeEntryFromDb(destReader.mDb, destReader.mTableName, toBeRemoved);
+        }
+        if (hotseatToBeAdded.isEmpty() && workspaceToBeAdded.isEmpty()) {
             return false;
         }
 
         // Sort the items by the reading order.
-        Collections.sort(hotseatDiff);
-        Collections.sort(workspaceDiff);
+        Collections.sort(hotseatToBeAdded);
+        Collections.sort(workspaceToBeAdded);
 
         // Migrate hotseat
         solveHotseatPlacement(db, srcReader,
-                destReader, context, destHotseatSize, hotseatItems, hotseatDiff);
+                destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded);
 
         // Migrate workspace.
         // First we create a collection of the screens
@@ -229,8 +256,8 @@
                 Log.d(TAG, "Migrating " + screenId);
             }
             solveGridPlacement(db, srcReader,
-                    destReader, context, screenId, trgX, trgY, workspaceDiff, false);
-            if (workspaceDiff.isEmpty()) {
+                    destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false);
+            if (workspaceToBeAdded.isEmpty()) {
                 break;
             }
         }
@@ -238,42 +265,37 @@
         // In case the new grid is smaller, there might be some leftover items that don't fit on
         // any of the screens, in this case we add them to new screens until all of them are placed.
         int screenId = destReader.mLastScreenId + 1;
-        while (!workspaceDiff.isEmpty()) {
+        while (!workspaceToBeAdded.isEmpty()) {
             solveGridPlacement(db, srcReader,
-                    destReader, context, screenId, trgX, trgY, workspaceDiff, preservePages);
+                    destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
             screenId++;
         }
 
         return true;
     }
 
-    /** Return what's in the src but not in the dest */
-    private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
-        Map<String, Integer> destIdSet = new HashMap<>();
-        for (DbEntry entry : dest) {
-            String entryID = entry.getEntryMigrationId();
-            if (destIdSet.containsKey(entryID)) {
-                destIdSet.put(entryID, destIdSet.get(entryID) + 1);
-            } else {
-                destIdSet.put(entryID, 1);
+    /**
+     * Calculate the differences between {@code src} (denoted by A) and {@code dest}
+     * (denoted by B).
+     * All DbEntry in A - B will be added to {@code toBeAdded}
+     * All DbEntry.id in B - A will be added to {@code toBeRemoved}
+     */
+    private static void calcDiff(@NonNull final List<DbEntry> src,
+            @NonNull final List<DbEntry> dest, @NonNull final List<DbEntry> toBeAdded,
+            @NonNull final IntArray toBeRemoved) {
+        src.forEach(entry -> {
+            if (!dest.contains(entry)) {
+                toBeAdded.add(entry);
             }
-        }
-        List<DbEntry> diff = new ArrayList<>();
-        for (DbEntry entry : src) {
-            String entryID = entry.getEntryMigrationId();
-            if (destIdSet.containsKey(entryID)) {
-                Integer count = destIdSet.get(entryID);
-                if (count <= 0) {
-                    diff.add(entry);
-                    destIdSet.remove(entryID);
-                } else {
-                    destIdSet.put(entryID, count - 1);
+        });
+        dest.forEach(entry -> {
+            if (!src.contains(entry)) {
+                toBeRemoved.add(entry.id);
+                if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+                    entry.mFolderItems.values().forEach(ids -> ids.forEach(toBeRemoved::add));
                 }
-            } else {
-                diff.add(entry);
             }
-        }
-        return diff;
+        });
     }
 
     private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
@@ -682,12 +704,12 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             DbEntry entry = (DbEntry) o;
-            return Objects.equals(mIntent, entry.mIntent);
+            return Objects.equals(getEntryMigrationId(), entry.getEntryMigrationId());
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mIntent);
+            return Objects.hash(getEntryMigrationId());
         }
 
         public void updateContentValues(ContentValues values) {
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index 7af1a13..5f93a66 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -20,8 +20,6 @@
 import com.android.launcher3.BaseActivity;
 
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -30,8 +28,15 @@
  */
 public final class ActivityTracker<T extends BaseActivity> {
 
+    public static final int TYPE_INIT = 0;
+    public static final int TYPE_DESTROY = 1;
+    public static final int TYPE_BOTH = 2;
+
     private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
-    private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
+    private CopyOnWriteArrayList<SchedulerCallback<T>> mActivityReadyCallbacks =
+            new CopyOnWriteArrayList<>();
+    private CopyOnWriteArrayList<SchedulerCallback<T>> mActivityDestroyedCallbacks =
+            new CopyOnWriteArrayList<>();
 
     @Nullable
     public <R extends T> R getCreatedActivity() {
@@ -42,32 +47,74 @@
         if (mCurrentActivity.get() == activity) {
             mCurrentActivity.clear();
         }
+        for (SchedulerCallback<T> cb : mActivityDestroyedCallbacks) {
+            cb.onActivityDestroyed();
+            unregisterCallback(cb, TYPE_DESTROY);
+        }
+    }
+
+    /** Registers an activity create callback. */
+    public void registerCallback(SchedulerCallback<T> callback) {
+        registerCallback(callback, TYPE_INIT);
     }
 
     /**
-     * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the
-     * activity is ready. If the activity is already created, this is called immediately.
+     * Call {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} when the
+     * activity is ready and/or {@link SchedulerCallback#onActivityDestroyed()} when the activity
+     * is destroyed.
      *
-     * The tracker maintains a strong ref to the callback, so it is up to the caller to return
-     * {@code false} in the callback OR to unregister the callback explicitly.
+     * If type is {@link ActivityTracker#TYPE_INIT} TYPE_INIT or
+     * {@link ActivityTracker#TYPE_BOTH} and the activity is already created, this
+     * {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} is called immediately.
      *
-     * @param callback The callback to call init() on when the activity is ready.
+     * If type is {@link ActivityTracker#TYPE_DESTROY} or
+     * {@link ActivityTracker#TYPE_BOTH} and the activity is already destroyed,
+     * {@link SchedulerCallback#onActivityDestroyed()} is called immediately.
+     *
+     * The tracker maintains a strong ref to the callbacks, so it is up to the caller to return
+     * {@code false} in {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} OR to
+     * unregister the callback explicitly.
+     *
+     * @param callback The callback to call init() or cleanUp() on when the activity is ready or
+     *                 destroyed.
+     * @param type whether to use this callback on activity create, destroy or both.
      */
-    public void registerCallback(SchedulerCallback<T> callback) {
+    public void registerCallback(SchedulerCallback<T> callback, int type) {
         T activity = mCurrentActivity.get();
-        mCallbacks.add(callback);
-        if (activity != null) {
-            if (!callback.init(activity, activity.isStarted())) {
-                unregisterCallback(callback);
+        if (type == TYPE_INIT || type == TYPE_BOTH) {
+            mActivityReadyCallbacks.add(callback);
+            if (activity != null) {
+                if (!callback.onActivityReady(activity, activity.isStarted())) {
+                    unregisterCallback(callback, TYPE_INIT);
+                }
+            }
+        }
+        if (type == TYPE_DESTROY || type == TYPE_BOTH) {
+            mActivityDestroyedCallbacks.add(callback);
+            if (activity == null) {
+                callback.onActivityDestroyed();
+                unregisterCallback(callback, TYPE_DESTROY);
             }
         }
     }
 
     /**
-     * Unregisters a registered callback.
+     * Unregisters a registered activity create callback.
      */
     public void unregisterCallback(SchedulerCallback<T> callback) {
-        mCallbacks.remove(callback);
+        unregisterCallback(callback, TYPE_INIT);
+    }
+
+    /**
+     * Unregisters a registered callback.
+     */
+    public void unregisterCallback(SchedulerCallback<T> callback, int type) {
+        if (type == TYPE_INIT || type == TYPE_BOTH) {
+            mActivityReadyCallbacks.remove(callback);
+        }
+        if (type == TYPE_DESTROY || type == TYPE_BOTH) {
+            mActivityDestroyedCallbacks.remove(callback);
+        }
     }
 
     public boolean handleCreate(T activity) {
@@ -81,8 +128,8 @@
 
     private boolean handleIntent(T activity, boolean alreadyOnHome) {
         boolean handled = false;
-        for (SchedulerCallback<T> cb : mCallbacks) {
-            if (!cb.init(activity, alreadyOnHome)) {
+        for (SchedulerCallback<T> cb : mActivityReadyCallbacks) {
+            if (!cb.onActivityReady(activity, alreadyOnHome)) {
                 // Callback doesn't want any more updates
                 unregisterCallback(cb);
             }
@@ -98,6 +145,11 @@
          * @param alreadyOnHome Whether the activity is already started.
          * @return Whether to continue receiving callbacks (i.e. if the activity is recreated).
          */
-        boolean init(T activity, boolean alreadyOnHome);
+        boolean onActivityReady(T activity, boolean alreadyOnHome);
+
+        /**
+         * Called then the activity gets destroyed.
+         */
+        default void onActivityDestroyed() { }
     }
 }
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 85d7bf9..76a186b 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -17,6 +17,7 @@
 
 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
@@ -183,15 +184,232 @@
         // Expected dest grid icons
         // _ _ _ _
         // 5 6 7 8
-        // 9 _ 10_
+        // 9 _ _ _
         // _ _ _ _
-        assertThat(locMap.size.toLong()).isEqualTo(6)
+        assertThat(locMap.size.toLong()).isEqualTo(5)
         assertThat(locMap[testPackage5]).isEqualTo(Point(0, 1))
         assertThat(locMap[testPackage6]).isEqualTo(Point(1, 1))
         assertThat(locMap[testPackage7]).isEqualTo(Point(2, 1))
         assertThat(locMap[testPackage8]).isEqualTo(Point(3, 1))
         assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2))
-        assertThat(locMap[testPackage10]).isEqualTo(Point(2, 2))
+    }
+
+    /**
+     * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is
+     * not needed anymore
+     */
+    @Test
+    @Throws(Exception::class)
+    fun testMigrationBackAndForth() {
+        // Hotseat items in grid A
+        // 1 2 _ 3 4
+        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)
+        // Workspace items in grid A
+        // _ _ _ _ _
+        // _ _ _ _ 5
+        // _ _ 6 _ 7
+        // _ _ 8 _ _
+        // _ _ _ _ _
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+
+        // Hotseat items in grid B
+        // 2 _ _ _
+        modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2)
+        // Workspace items in grid B
+        // _ _ _ _
+        // _ _ _ 10
+        // _ _ _ _
+        // _ _ _ _
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10)
+
+        idp.numDatabaseHotseatIcons = 4
+        idp.numColumns = 4
+        idp.numRows = 4
+        val readerGridA = DbReader(db, TMP_TABLE, context, validPackages)
+        val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
+        // migrate from A -> B
+        GridSizeMigrationUtil.migrate(
+                context,
+                db,
+                readerGridA,
+                readerGridB,
+                idp.numDatabaseHotseatIcons,
+                Point(idp.numColumns, idp.numRows),
+                DeviceGridState(context),
+                DeviceGridState(idp)
+        )
+
+        // Check hotseat items in grid B
+        var c = context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, INTENT),
+                "container=$CONTAINER_HOTSEAT",
+                null,
+                SCREEN,
+                null
+        ) ?: throw IllegalStateException()
+        // Expected hotseat items in grid B
+        // 2 1 3 4
+        verifyHotseat(c, idp,
+                mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList())
+
+        // Check workspace items in grid B
+        c = context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, CELLX, CELLY, INTENT),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+        ) ?: throw IllegalStateException()
+        var locMap = parseLocMap(context, c)
+        // Expected items in grid B
+        // _ _ _ _
+        // 5 6 7 8
+        // _ _ _ _
+        // _ _ _ _
+        assertThat(locMap.size.toLong()).isEqualTo(4)
+        assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1))
+        assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1))
+        assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 2, 1))
+        assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
+
+        // add item in B
+        modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9)
+
+        // migrate from B -> A
+        GridSizeMigrationUtil.migrate(
+                context,
+                db,
+                readerGridB,
+                readerGridA,
+                5,
+                Point(5, 5),
+                DeviceGridState(idp),
+                DeviceGridState(context)
+        )
+        // Check hotseat items in grid A
+        c = context.contentResolver.query(
+                TMP_CONTENT_URI,
+                arrayOf(SCREEN, INTENT),
+                "container=$CONTAINER_HOTSEAT",
+                null,
+                SCREEN,
+                null
+        ) ?: throw IllegalStateException()
+        // Expected hotseat items in grid A
+        // 1 2 _ 3 4
+        verifyHotseat(c, idp, mutableListOf(
+                testPackage1, testPackage2, null, testPackage3, testPackage4).toList())
+
+        // Check workspace items in grid A
+        c = context.contentResolver.query(
+                TMP_CONTENT_URI,
+                arrayOf(SCREEN, CELLX, CELLY, INTENT),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+        ) ?: throw IllegalStateException()
+        locMap = parseLocMap(context, c)
+        // Expected workspace items in grid A
+        // _ _ _ _ _
+        // _ _ _ _ 5
+        // 9 _ 6 _ 7
+        // _ _ 8 _ _
+        // _ _ _ _ _
+        assertThat(locMap.size.toLong()).isEqualTo(5)
+        // Verify items that existed in grid A remains in same position
+        assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 4, 1))
+        assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 2, 2))
+        assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 4, 2))
+        assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 2, 3))
+        // Verify items that didn't exist in grid A are added in new screen
+        assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
+
+        // remove item from B
+        modelHelper.deleteItem(7, TMP_TABLE)
+
+        // migrate from A -> B
+        GridSizeMigrationUtil.migrate(
+                context,
+                db,
+                readerGridA,
+                readerGridB,
+                idp.numDatabaseHotseatIcons,
+                Point(idp.numColumns, idp.numRows),
+                DeviceGridState(context),
+                DeviceGridState(idp)
+        )
+
+        // Check hotseat items in grid B
+        c = context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, INTENT),
+                "container=$CONTAINER_HOTSEAT",
+                null,
+                SCREEN,
+                null
+        ) ?: throw IllegalStateException()
+        // Expected hotseat items in grid B
+        // 2 1 3 4
+        verifyHotseat(c, idp,
+                mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList())
+
+        // Check workspace items in grid B
+        c = context.contentResolver.query(
+                CONTENT_URI,
+                arrayOf(SCREEN, CELLX, CELLY, INTENT),
+                "container=$CONTAINER_DESKTOP",
+                null,
+                null,
+                null
+        ) ?: throw IllegalStateException()
+        locMap = parseLocMap(context, c)
+        // Expected workspace items in grid B
+        // _ _ _ _
+        // 5 6 _ 8
+        // 9 _ _ _
+        // _ _ _ _
+        assertThat(locMap.size.toLong()).isEqualTo(4)
+        assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1))
+        assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1))
+        assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
+        assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
+    }
+
+    private fun verifyHotseat(c: Cursor, idp: InvariantDeviceProfile, expected: List<String?>) {
+        assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons)
+        val screenIndex = c.getColumnIndex(SCREEN)
+        val intentIndex = c.getColumnIndex(INTENT)
+        expected.forEachIndexed { idx, pkg ->
+            if (pkg == null) return@forEachIndexed
+            c.moveToNext()
+            assertThat(c.getInt(screenIndex).toLong()).isEqualTo(idx)
+            assertThat(c.getString(intentIndex)).contains(pkg)
+        }
+        c.close()
+    }
+
+    private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> {
+        // Check workspace items
+        val intentIndex = c.getColumnIndex(INTENT)
+        val screenIndex = c.getColumnIndex(SCREEN)
+        val cellXIndex = c.getColumnIndex(CELLX)
+        val cellYIndex = c.getColumnIndex(CELLY)
+        val locMap = mutableMapOf<String, Triple<Int, Int, Int>>()
+        while (c.moveToNext()) {
+            locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] =
+                    Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex))
+        }
+        c.close()
+        return locMap.toMap()
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index e7e551f..93bf312 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -362,6 +362,12 @@
         sandboxContext.getContentResolver().insert(contentUri, values);
     }
 
+    public void deleteItem(int itemId, @NonNull final String tableName) {
+        final Uri uri = Uri.parse("content://"
+                + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId);
+        sandboxContext.getContentResolver().delete(uri, null, null);
+    }
+
     public int[][][] createGrid(int[][][] typeArray) {
         return createGrid(typeArray, 1);
     }