Merge "Use pinned taskbar with desktop tasks on freeform displays" into main
diff --git a/OWNERS b/OWNERS
index bed2acd..3f7a780 100644
--- a/OWNERS
+++ b/OWNERS
@@ -8,7 +8,6 @@
hyunyoungs@google.com
vadimt@google.com
winsonc@google.com
-awickham@google.com
agvard@google.com
# Launcher workspace eng team
@@ -21,6 +20,7 @@
pinyaoting@google.com
andonian@google.com
sihua@google.com
+abegovic@google.com
# Multitasking eng team
tracyzhou@google.com
@@ -55,11 +55,25 @@
twickham@google.com
victortulias@google.com
+## Note: some of the below overlap and also work on other integrations like Circle to Search.
+
+# All Apps / QSB team
+awickham@google.com
+brdayauon@google.com
+ganjam@google.com
+kylim@google.com
+
+# Smartspace team
+xilei@google.com
+davidct@google.com
+iamiam@google.com
+jiuyu@google.com
+
per-file FeatureFlags.java, globs = set noparent
-per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
+per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com, abegovic@google.com
per-file DeviceConfigWrapper.java, globs = set noparent
-per-file DeviceConfigWrapper.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com
+per-file DeviceConfigWrapper.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, abegovic@google.com
# Predictive Back
per-file LauncherBackAnimationController.java = shanh@google.com, gallmann@google.com
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 72b3c27..9fa2f50 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -584,3 +584,20 @@
description: "Changes mouse interaction behavior"
bug: "388897603"
}
+
+flag {
+ name: "enable_alt_tab_kqs_on_connected_displays"
+ namespace: "lse_desktop_experience"
+ description: "Enable Alt + Tab KQS support on connected displays"
+ bug: "394007677"
+}
+
+flag {
+ name: "expressive_theme_in_taskbar_and_navigation"
+ namespace: "launcher"
+ description: "Enables the expressive theme and GSF font styles for Taskbar and Gesture Navigation"
+ bug: "394613212"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/aconfig/launcher_accessibility.aconfig b/aconfig/launcher_accessibility.aconfig
new file mode 100644
index 0000000..afee8fe
--- /dev/null
+++ b/aconfig/launcher_accessibility.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.launcher3"
+container: "system"
+
+flag {
+ name: "remove_exclude_from_screen_magnification_flag_usage"
+ namespace: "accessibility"
+ description: "Remove the WindowManager PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION flag usage in Launcher"
+ bug: "369019568"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 4c4a403..c71308f 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vormvry"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Rekenaar"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Skuif na eksterne skerm"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Werkskerm"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programgebruikinstellings"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 2716fd5..a7b9957 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ነፃ ቅጽ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ዴስክቶፕ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ወደ ውጫዊ ማሳያ አንቀሳቅስ"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ዴስክቶፕ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 281125d..5b61fa7 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"شكل مجاني"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"الكمبيوتر المكتبي"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"نقل التطبيق إلى شاشة خارجية"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"إغلاق"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"كمبيوتر مكتبي"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ما مِن عناصر تم استخدامها مؤخرًا"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 4292a76..d7cb836 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ডেস্কটপ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"বাহ্যিক ডিছপ্লে’লৈ নিয়ক"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"বন্ধ কৰক"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"ডেস্কটপ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"কোনো শেহতীয়া বস্তু নাই"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"এপে ব্যৱহাৰ কৰা ডেটাৰ ছেটিং"</string>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index a7bd553..ab57177 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Sərbəst rejim"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Masaüstü"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Xarici displeyə köçürün"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Masaüstü"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tətbiq istifadə ayarları"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index ef46503..58a9c6b 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Računar"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Premestite na spoljni ekran"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Zatvori"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Računari"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Podešavanja korišćenja aplikacije"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 205fe9d..08d855c 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Адвольная форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Працоўны стол"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Перамясціць на знешні дысплэй"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Працоўны стол"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налады выкарыстання праграмы"</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 048806f..f67e8e4 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Свободна форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"За компютър"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Преместване към външния екран"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Настолен компютър"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Няма скорошни елементи"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки за използването на приложенията"</string>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index dec7443..8ff5d76 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ফ্রি-ফর্ম"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ডেস্কটপ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"এক্সটার্নাল ডিসপ্লেতে সরিয়ে নিয়ে যান"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ডেস্কটপ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"কোনও সাম্প্রতিক আইটেম নেই"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"অ্যাপ ব্যবহারের সেটিংস"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 626279f..9553e28 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodan oblik"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Radna površina"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Premještanje na vanjski ekran"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Zatvori"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Radna površina"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke korištenja aplikacije"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 810e8f8..44ef6c3 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format lliure"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escriptori"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mou a la pantalla externa"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Escriptori"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No hi ha cap element recent"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuració d\'ús d\'aplicacions"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 9fd2fa5..d809cbe 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Neomezený režim"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Počítač"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Přesunout na externí displej"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Zavřít"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Počítač"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Žádné položky z nedávné doby"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index b2a5f33..224ac11 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frit format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Computertilstand"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Flyt til ekstern skærm"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Computertilstand"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 93fba3b..40c1cce 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktopmodus"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Auf externes Display verschieben"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktopmodus"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
@@ -106,7 +108,7 @@
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Tutorial zur Bedienung überspringen?"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Du findest es später auch in der <xliff:g id="NAME">%1$s</xliff:g> App"</string>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Abbrechen"</string>
- <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Überspringen"</string>
+ <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Überspringen"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Bildschirm drehen"</string>
<string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Informationen zur Taskleiste"</string>
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"App zur Seite ziehen, um zwei Apps gleichzeitig zu nutzen"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 960831e..046e047 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Ελεύθερη μορφή"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Υπολογιστής"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Μετακίνηση σε εξωτερική οθόνη"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Υπολογιστής"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index bf115f2..0c8384c 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Move to external display"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index cd6abc3..4efd369 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Move to external display"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Close"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index bf115f2..0c8384c 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Move to external display"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index bf115f2..0c8384c 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Move to external display"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 0955f28..5ba3212 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escritorio"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mover a pantalla externa"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Cerrar"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Computadoras"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string>
@@ -89,7 +90,7 @@
<string name="allset_title" msgid="5021126669778966707">"Todo listo"</string>
<string name="allset_hint" msgid="459504134589971527">"Desliza el dedo hacia arriba para ir a la página principal"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Presiona el botón de inicio para ir a la pantalla principal"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"Ya puedes comenzar a usar tu <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Ya puedes comenzar a usar tu <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">"Configuración de navegación del sistema"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Compartir"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index a5ac1d2..6c412f7 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escritorio"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mover a pantalla externa"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Ordenador"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No hay nada reciente"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ajustes de uso de la aplicación"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 0af4d9c..d4e041a 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vabavorm"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Lauaarvuti režiim"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Liikuge välisele ekraanile"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Sule"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Töölaud"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Rakenduse kasutuse seaded"</string>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 9ee1255..892089d 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Modu librea"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Ordenagailua"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Eraman kanpoko pantailara"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Mahaigaina"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Aplikazioen erabileraren ezarpenak"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index d1ca8ed..7dcdb7f 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"حالت رایانه"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"انتقال به نمایشگر خارجی"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"بستن"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"رایانه"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"چیز جدیدی اینجا نیست"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"تنظیمات استفاده از برنامه"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 13750dc..0e360d0 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vapaamuotoinen"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Tietokone"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Siirrä ulkoiselle näytölle"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Tietokone"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ei viimeaikaisia kohteita"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Sovelluksen käyttöasetukset"</string>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index b83e33c..e7619c2 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forme libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Bureau"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Passer à un écran externe"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Fermer"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Bureau"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'appli"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 66102d5..9982b25 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Mode ordinateur"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Déplacer vers l\'écran externe"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Fermer"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Mode ordinateur"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 16c448c..f4260f3 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escritorio"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mover á pantalla externa"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Ordenador"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Non hai elementos recentes"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración do uso de aplicacións"</string>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 0cc6091..2797f82 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ફ્રિફોર્મ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ડેસ્કટૉપ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"બાહ્ય ડિસ્પ્લે પર ખસેડો"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ડેસ્કટૉપ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"તાજેતરની કોઈ આઇટમ નથી"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ઍપ વપરાશનું સેટિંગ"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 2135dac..5ed6ef5 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ़्रीफ़ॉर्म"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"डेस्कटॉप"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"बाहरी डिसप्ले पर जाएं"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"डेस्कटॉप"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"हाल ही का कोई आइटम नहीं है"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 0fd3123..67fe240 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Računalo"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Premještanje na vanjski zaslon"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Zatvori"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Radna površina"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 92ada47..4f15c7d 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Szabad forma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Asztali"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Áthelyezés külső kijelzőre"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Asztali"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nincsenek mostanában használt elemek"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Alkalmazáshasználati beállítások"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 7751ea1..edaa5bd 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Կամայական ձև"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Համակարգիչ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Տեղափոխել արտաքին էկրան"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Համակարգիչ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Այստեղ դեռ ոչինչ չկա"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Հավելվածի օգտագործման կարգավորումներ"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 1909376..4b2ffd7 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format bebas"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Pindahkan ke layar eksternal"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item yang baru dibuka"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 30d6dad..3619a2d 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frjálst snið"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Tölva"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Færa í annað tæki"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Tölva"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Engin nýleg atriði"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Notkunarstillingar forrits"</string>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 7f80107..17aefa4 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libera"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Sposta sul display esterno"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index f93f741..1cba30f 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"מצב חופשי"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"במחשב"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"העברה למסך חיצוני"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"מחשב"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"הגדרות שימוש באפליקציה"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index e3625cb..ded498a 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"フリーフォーム"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"デスクトップ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"外部ディスプレイに移動する"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"閉じる"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"デスクトップ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 623319d..1604495 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"თავისუფალი ფორმა"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"დესკტოპი"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"გარე ეკრანზე გადასვლა"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"დახურვა"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"დესკტოპი"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"აპების გამოყენების პარამეტრები"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index f707689..b95e708 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Еркін форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Компьютер"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Сыртқы дисплейге ауыстыру"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Жұмыс үстелі"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Қолданбаны пайдалану параметрлері"</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 25e1ef2..1fdcde9 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"មុខងារទម្រង់សេរី"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ដែសថប"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ផ្លាស់ទីទៅផ្ទាំងអេក្រង់ខាងក្រៅ"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"បិទ"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"អេក្រង់ដើម"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់ការប្រើប្រាស់កម្មវិធី"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index ed4b806..19d3ec9 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ಮುಕ್ತಸ್ವರೂಪ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ಡೆಸ್ಕ್ಟಾಪ್"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ಬಾಹ್ಯ ಡಿಸ್ಪ್ಲೇಗೆ ಸರಿಸಿ"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ಡೆಸ್ಕ್ಟಾಪ್"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ಆ್ಯಪ್ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index d11e0b2..2e89174 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"자유 형식"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"데스크톱"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"외부 디스플레이로 이동"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"데스크톱"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"최근 항목이 없습니다."</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"앱 사용 설정"</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 3d59332..ac2b06d 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Эркин форма режими"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Компьютер"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Тышкы экранга жылдыруу"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Компьютер"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу параметрлери"</string>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index e480766..2ebaa7a 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ຮູບແບບອິດສະຫລະ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ເດັສທັອບ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ຍ້າຍໄປຫາຈໍສະແດງຜົນພາຍນອກ"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"ປິດ"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"ເດັສທັອບ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ການຕັ້ງຄ່າການນຳໃຊ້ແອັບ"</string>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 2bcbb6d..b57fee4 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Laisva forma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Stalinis kompiuteris"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Perkelkite į išorinį ekraną"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Stalinis kompiuteris"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nėra jokių naujausių elementų"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programos naudojimo nustatymai"</string>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 7d88140..07e9e1a 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Brīva forma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Dators"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Pārvietošana uz ārējo displeju"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Darbvirsma"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nav nesenu vienumu."</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Lietotņu izmantošanas iestatījumi"</string>
@@ -89,7 +91,7 @@
<string name="allset_title" msgid="5021126669778966707">"Gatavs!"</string>
<string name="allset_hint" msgid="459504134589971527">"Velciet augšup, lai pārietu uz sākuma ekrānu"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Pieskarieties pogai Sākums, lai dotos uz sākuma ekrānu"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"Varat sākt izmantot savu ierīci (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Jūsu <xliff:g id="DEVICE">%1$s</xliff:g> ir gatava lietošanai"</string>
<string name="default_device_name" msgid="6660656727127422487">"ierīce"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Sistēmas navigācijas iestatījumi"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Kopīgot"</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 632196b..0a133a3 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Работна површина"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Префрлете се на надворешниот екран"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"За компјутер"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 2246889..849315b 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ഫ്രീഫോം"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ഡെസ്ക്ടോപ്പ്"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ബാഹ്യ ഡിസ്പ്ലേയിലേക്ക് നീക്കുക"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"അടയ്ക്കുക"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"ഡെസ്ക്ടോപ്പ്"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ആപ്പ് ഉപയോഗ ക്രമീകരണം"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 94adf88..c50e87a 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Чөлөөтэй хувьсах"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Компьютер"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Гадаад дэлгэц рүү зөөх"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Дэлгэц"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index d2fdb42..63f4317 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रीफॉर्म"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"डेस्कटॉप"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"बाह्य डिस्प्लेवर हलवा"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"डेस्कटॉप"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अॅप वापर सेटिंग्ज"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 66c5bdb..b039666 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Bentuk bebas"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Alihkan kepada paparan luaran"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Tutup"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tetapan penggunaan apl"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index c9ac441..9df3d66 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"အလွတ်ပုံစံ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ဒက်စ်တော့"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ပြင်ပဖန်သားပြင်သို့ ရွှေ့ရန်"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ဒက်စ်တော့"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
@@ -89,7 +91,7 @@
<string name="allset_title" msgid="5021126669778966707">"အားလုံး အဆင်သင့်ပါ။"</string>
<string name="allset_hint" msgid="459504134589971527">"ပင်မစာမျက်နှာသို့သွားရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"ပင်မစာမျက်နှာသို့ သွားရန် ပင်မခလုတ်ကို တို့ပါ"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> ကို စသုံးရန် အသင့်ဖြစ်ပါပြီ"</string>
+ <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>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index aa19606..d05884f 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Skrivebord"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Flytt til ekstern skjerm"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Skrivebord"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ingen nylige elementer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Innstillinger for appbruk"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 4793ad3..6efdca9 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -23,8 +23,10 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रिफर्म"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"डेस्कटप"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"सारेर बाह्य डिस्प्लेमा लैजानुहोस्"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"डेस्कटप"</string>
- <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि सामग्री छैन"</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>
@@ -125,7 +127,7 @@
<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>
- <string name="taskbar_button_notifications" msgid="7471740351507357318">"सूचनाहरू"</string>
+ <string name="taskbar_button_notifications" msgid="7471740351507357318">"नोटिफिकेसनहरू"</string>
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"द्रुत सेटिङ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार देखाइएको छ"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 3cd9457..6cf9683 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vrije vorm"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Verplaatsen naar extern scherm"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Sluiten"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Geen recente items"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Instellingen voor app-gebruik"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 20420d9..66cbcdd 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ଡେସ୍କଟପ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ଡେସ୍କଟପ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ବର୍ତ୍ତମାନର କୌଣସି ଆଇଟମ ନାହିଁ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ ବ୍ୟବହାର ସେଟିଂସ"</string>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 5cab154..8489fd1 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"ਫ੍ਰੀਫਾਰਮ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ਡੈਸਕਟਾਪ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ਬਾਹਰੀ ਡਿਸਪਲੇ \'ਤੇ ਜਾਓ"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"ਬੰਦ ਕਰੋ"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"ਡੈਸਕਟਾਪ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮ ਨਹੀਂ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ਐਪ ਵਰਤੋਂ ਦੀਆਂ ਸੈਟਿੰਗਾਂ"</string>
@@ -89,7 +90,7 @@
<string name="allset_title" msgid="5021126669778966707">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਤਿਆਰ!"</string>
<string name="allset_hint" msgid="459504134589971527">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਜਾਣ ਲਈ ਹੋਮ ਬਟਨ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"ਤੁਸੀਂ ਆਪਣਾ <xliff:g id="DEVICE">%1$s</xliff:g> ਵਰਤਣ ਲਈ ਤਿਆਰ ਹੋ"</string>
+ <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>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 94e39d0..b571ba2 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Tryb dowolny"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Pulpit"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Przenieś na wyświetlacz zewnętrzny"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Pulpit"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Brak ostatnich elementów"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ustawienia użycia aplikacji"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index cace987..84a4f58 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Computador"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mover para o ecrã externo"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Fechar"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Computador"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Definições de utilização de aplicações"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 434cf6b..bb7d97d 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Modo área de trabalho"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mover para a tela externa"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Computador"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index ac25e6d..856bef7 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formă liberă"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Mută pe ecranul extern"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Computer"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setări de utilizare a aplicației"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 9700f16..9ebbc02 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Произвольная форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Мультиоконный режим"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Перенести на внешний дисплей"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Закрыть"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Мультиоконный режим"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Здесь пока ничего нет."</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки использования приложения"</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index cd33d09..3b1cada 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ඩෙස්ක්ටොපය"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"බාහිර සංදර්ශකය වෙත ගෙන යන්න"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"ඩෙස්ක්ටොපය"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"යෙදුම් භාවිත සැකසීම්"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 7f44a23..bf9adff 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Voľný režim"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Počítač"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Presunúť na externú obrazovku"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Zavrieť"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Počítač"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Žiadne nedávne položky"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavenia využívania aplikácie"</string>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index f504abc..440c0f1 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Prosta oblika"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Namizni računalnik"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Premik v zunanji zaslon"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Zapri"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Namizni način"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ni nedavnih elementov"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavitve uporabe aplikacij"</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 36769cb..8f39f65 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formë e lirë"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktopi"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Zhvendose tek ekrani i jashtëm"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktopi"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cilësimet e përdorimit të aplikacionit"</string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 0d89f74..e810d5a 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Слободни облик"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Рачунар"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Преместите на спољни екран"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Затвори"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Рачунари"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Нема недавних ставки"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Подешавања коришћења апликације"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 9941818..c33de8a 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Skrivbordsläge"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Flytta till extern skärm"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Skrivbordsläge"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Listan är tom"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index c7e5c30..543663f 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Muundo huru"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Kompyuta ya mezani"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Hamishia programu kwenye skrini ya nje"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Kompyuta ya Mezani"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Hakuna vipengee vya hivi karibuni"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mipangilio ya matumizi ya programu"</string>
@@ -46,9 +48,9 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Mapendekezo ya programu yamewashwa"</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Umezima mapendekezo ya programu"</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Programu iliyotabiriwa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="gesture_tutorial_title" msgid="2750751261768388354">"Mafunzo ya Usogezaji kwa Kutumia Ishara"</string>
+ <string name="gesture_tutorial_title" msgid="2750751261768388354">"Mafunzo ya Usogezaji kwa Kutumia Miguso"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zungusha kifaa chako"</string>
- <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Tafadhali zungusha kifaa chako ili ukamilishe mafunzo ya usogezaji kwa kutumia ishara"</string>
+ <string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Tafadhali zungusha kifaa chako ili ukamilishe mafunzo ya usogezaji kwa kutumia miguso"</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Hakikisha unatelezesha kidole kutoka ukingo wa kulia au kushoto kabisa"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Hakikisha unatelezesha kidole kutoka ukingo wa kulia au kushoto hadi katikati ya skrini na uachilie"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Umejifunza jinsi ya kutelezesha kidole kuanzia kulia ili kurudi nyuma. Sasa jifunze jinsi ya kubadilisha programu."</string>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 042679c..1e6cb9b 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"குறிப்பிட்ட வடிவமில்லாத பயன்முறை"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"டெஸ்க்டாப்"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"வெளிப்புற டிஸ்ப்ளேவிற்கு நகர்த்துதல்"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"டெஸ்க்டாப்"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"சமீபத்தியவை எதுவுமில்லை"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ஆப்ஸ் உபயோக அமைப்புகள்"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 081708c..74e59ba 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"సంప్రదాయేతర"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"డెస్క్టాప్"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ఎక్స్టర్నల్ డిస్ప్లేకు తరలించండి"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"డెస్క్టాప్"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి ఐటెమ్లు ఏవీ లేవు"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్లు"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 61dfaa6..03852ff 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"รูปแบบอิสระ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"เดสก์ท็อป"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"ย้ายไปยังจอแสดงผลภายนอก"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"ปิด"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"เดสก์ท็อป"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 8374628..1b3696e 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Ilipat sa external na display"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Isara"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Walang kamakailang item"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mga setting ng paggamit ng app"</string>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 9a1bf54..ac06369 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Serbest çalışma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Masaüstü"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Harici ekrana taşı"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Masaüstü"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Yeni öğe yok"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Uygulama kullanım ayarları"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 1036700..2d27bb9 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Довільна форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Робочий стіл"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Перемістити на зовнішній екран"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Комп’ютер"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Немає нещодавніх додатків"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налаштування використання додатка"</string>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index fde634f..0a3e122 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"فری فارم"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ڈیسک ٹاپ"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"بیرونی ڈسپلے پر متقل کریں"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"بند کریں"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"ڈیسک ٹاپ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"کوئی حالیہ آئٹم نہیں"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ایپ کے استعمال کی ترتیبات"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index ab514b3..1cd2e87 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -23,6 +23,7 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Erkin shakl"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Tashqi displeyga olish"</string>
+ <string name="recent_task_option_close" msgid="942942499021777264">"Yopish"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Desktop"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ilovadan foydalanish sozlamalari"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index f35138b..e49f7e3 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"Dạng tự do"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Máy tính"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Chuyển sang màn hình ngoài"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Máy tính"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Không có mục gần đây nào"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 9665800..c1cf2fe 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由窗口"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"桌面"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"移至外接显示屏"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"桌面设备"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"近期没有任何内容"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"应用使用设置"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index d65aba1..393565a 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"桌面"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"移至外部顯示屏"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"桌面"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 0b94911..fbd465d 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"電腦模式"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"移至外接螢幕"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"電腦模式"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 8ad0b94..39b4873 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -23,6 +23,8 @@
<string name="recent_task_option_freeform" msgid="48863056265284071">"I-Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Ideskithophu"</string>
<string name="recent_task_option_external_display" msgid="4533840664313389484">"Hambisa esibonisini sangaphandle"</string>
+ <!-- no translation found for recent_task_option_close (942942499021777264) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Ideskithophu"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Izilungiselelo zokusetshenziswa kohlelo lokusebenza"</string>
diff --git a/quickstep/src/com/android/launcher3/model/data/TaskViewItemInfo.kt b/quickstep/src/com/android/launcher3/model/data/TaskViewItemInfo.kt
index ee28d7a..c201ab1 100644
--- a/quickstep/src/com/android/launcher3/model/data/TaskViewItemInfo.kt
+++ b/quickstep/src/com/android/launcher3/model/data/TaskViewItemInfo.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.content.Intent
+import android.os.Process
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.Companion.PRIVATE
import com.android.launcher3.Flags.privateSpaceRestrictAccessibilityDrag
@@ -26,34 +27,40 @@
import com.android.launcher3.pm.UserCache
import com.android.quickstep.TaskUtils
import com.android.quickstep.views.TaskContainer
+import com.android.quickstep.views.TaskView
-class TaskViewItemInfo(taskContainer: TaskContainer) : WorkspaceItemInfo() {
+class TaskViewItemInfo(taskView: TaskView, taskContainer: TaskContainer?) : WorkspaceItemInfo() {
@VisibleForTesting(otherwise = PRIVATE) val taskViewAtom: LauncherAtom.TaskView
init {
itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK
container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER
- val componentKey = TaskUtils.getLaunchComponentKeyForTask(taskContainer.task.key)
- user = componentKey.user
- intent = Intent().setComponent(componentKey.componentName)
- title = taskContainer.task.title
- if (privateSpaceRestrictAccessibilityDrag()) {
- if (
- UserCache.getInstance(taskContainer.taskView.context)
- .getUserInfo(componentKey.user)
- .isPrivate
- ) {
- runtimeStatusFlags = runtimeStatusFlags or ItemInfoWithIcon.FLAG_NOT_PINNABLE
+ val componentName: String
+ if (taskContainer != null) {
+ val componentKey = TaskUtils.getLaunchComponentKeyForTask(taskContainer.task.key)
+ user = componentKey.user
+ intent = Intent().setComponent(componentKey.componentName)
+ title = taskContainer.task.title
+ if (privateSpaceRestrictAccessibilityDrag()) {
+ if (
+ UserCache.getInstance(taskView.context).getUserInfo(componentKey.user).isPrivate
+ ) {
+ runtimeStatusFlags = runtimeStatusFlags or ItemInfoWithIcon.FLAG_NOT_PINNABLE
+ }
}
+ componentName = componentKey.componentName.flattenToShortString()
+ } else {
+ user = Process.myUserHandle()
+ intent = Intent()
+ componentName = ""
}
taskViewAtom =
createTaskViewAtom(
- type = taskContainer.taskView.type.ordinal,
- index =
- taskContainer.taskView.recentsView?.indexOfChild(taskContainer.taskView) ?: -1,
- componentName = componentKey.componentName.flattenToShortString(),
- cardinality = taskContainer.taskView.taskContainers.size,
+ type = taskView.type.ordinal,
+ index = taskView.recentsView?.indexOfChild(taskView) ?: -1,
+ componentName,
+ cardinality = taskView.taskContainers.size,
)
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index 0703a61..3c4bc91 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -33,6 +33,7 @@
import com.android.quickstep.GestureState.GestureEndTarget
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.fallback.RecentsState
+import com.android.wm.shell.desktopmode.DisplayDeskState
import com.android.wm.shell.desktopmode.IDesktopTaskListener.Stub
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import java.io.PrintWriter
@@ -365,6 +366,11 @@
) : Stub() {
private val controller = WeakReference(controller)
+ // TODO: b/392986431 - Implement the new desks APIs.
+ override fun onListenerConnected(
+ displayDeskStates: Array<DisplayDeskState>,
+ ) {}
+
override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
if (displayId != this.displayId) return
Executors.MAIN_EXECUTOR.execute {
@@ -398,6 +404,15 @@
override fun onEnterDesktopModeTransitionStarted(transitionDuration: Int) {}
override fun onExitDesktopModeTransitionStarted(transitionDuration: Int) {}
+
+ // TODO: b/392986431 - Implement all the below new desks APIs.
+ override fun onCanCreateDesksChanged(displayId: Int, canCreateDesks: Boolean) {}
+
+ override fun onDeskAdded(displayId: Int, deskId: Int) {}
+
+ override fun onDeskRemoved(displayId: Int, deskId: Int) {}
+
+ override fun onActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {}
}
/** A listener for Taskbar in Desktop Mode. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 2e48910..ee5b8d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -74,6 +74,7 @@
import android.graphics.drawable.RotateDrawable;
import android.inputmethodservice.InputMethodService;
import android.os.Handler;
+import android.os.SystemProperties;
import android.util.Property;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -191,6 +192,7 @@
private final int mDarkIconColorOnWorkspace;
/** Color to use for navbar buttons, if they are on on a Taskbar surface background. */
private final int mOnBackgroundIconColor;
+ private final boolean mIsExpressiveThemeEnabled;
private @Nullable Animator mNavBarLocationAnimator;
private @Nullable BubbleBarLocation mBubbleBarTargetLocation;
@@ -272,6 +274,9 @@
if (mContext.isPhoneMode()) {
mTaskbarTransitions = new TaskbarTransitions(mContext, mNavButtonsView);
}
+ String SUWTheme = SystemProperties.get("setupwizard.theme", "");
+ mIsExpressiveThemeEnabled = SUWTheme.equals("glif_expressive")
+ || SUWTheme.equals("glif_expressive_light");
}
/**
@@ -336,10 +341,10 @@
// Start at 1 because relevant flags are unset at init.
mOnBackgroundNavButtonColorOverrideMultiplier.value = 1;
- // Force nav buttons (specifically back button) to be visible during setup wizard.
- boolean isInSetup = !mContext.isUserSetupComplete();
+ // Potentially force the back button to be visible during setup wizard.
+ boolean shouldShowInSetup = !mContext.isUserSetupComplete() && !mIsExpressiveThemeEnabled;
boolean isInKidsMode = mContext.isNavBarKidsModeActive();
- boolean alwaysShowButtons = isThreeButtonNav || isInSetup;
+ boolean alwaysShowButtons = isThreeButtonNav || shouldShowInSetup;
// Make sure to remove nav bar buttons translation when any of the following occur:
// - Notification shade is expanded
@@ -930,6 +935,10 @@
}
private void handleSetupUi() {
+ // Setup wizard handles the UI when the expressive theme is enabled.
+ if (mIsExpressiveThemeEnabled) {
+ return;
+ }
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 3963d40..9d1fc15 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -29,6 +29,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.Flags.removeExcludeFromScreenMagnificationFlagUsage;
import static com.android.launcher3.Utilities.calculateTextHeight;
import static com.android.launcher3.Utilities.isRunningInTestHarness;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
@@ -1906,6 +1907,10 @@
return;
}
+ if (removeExcludeFromScreenMagnificationFlagUsage()) {
+ return;
+ }
+
mIsExcludeFromMagnificationRegion = exclude;
if (exclude) {
mWindowLayoutParams.privateFlags |=
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 36185b1..ea2dec1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -116,7 +116,6 @@
Settings.Secure.NAV_BAR_KIDS_MODE);
private final Context mParentContext;
- private final @Nullable Context mNavigationBarPanelContext;
private final TaskbarNavButtonController mDefaultNavButtonController;
private final ComponentCallbacks mDefaultComponentCallbacks;
@@ -247,11 +246,6 @@
mParentContext = context;
createWindowContext(context.getDisplayId());
mAllAppsActionManager = allAppsActionManager;
- Display display = context.getSystemService(DisplayManager.class).getDisplay(
- getDefaultDisplayId());
- mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
- ? context.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
- : null;
if (enableTaskbarNoRecreate()) {
createTaskbarRootLayout(getDefaultDisplayId());
}
@@ -861,8 +855,14 @@
* Creates a {@link TaskbarActivityContext} for the given display and adds it to the map.
*/
private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
+ Display display = mParentContext.getSystemService(DisplayManager.class).getDisplay(
+ displayId);
+ Context navigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ ? mParentContext.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
+ : null;
+
TaskbarActivityContext newTaskbar = new TaskbarActivityContext(getWindowContext(displayId),
- mNavigationBarPanelContext, dp, mDefaultNavButtonController,
+ navigationBarPanelContext, dp, mDefaultNavButtonController,
mUnfoldProgressProvider, isDefaultDisplay(displayId),
SystemUiProxy.INSTANCE.get(getPrimaryWindowContext()));
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index 22a3630..e032430 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -17,6 +17,7 @@
package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
+import android.os.SystemProperties
import android.view.Gravity
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
@@ -39,7 +40,7 @@
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
AbstractNavButtonLayoutter(
resources,
@@ -48,11 +49,15 @@
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
private val mNavButtonsView = navButtonsView
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
+ val SUWTheme = SystemProperties.get("setupwizard.theme", "")
+ if (SUWTheme == "glif_expressive" || SUWTheme == "glif_expressive_light") {
+ return
+ }
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
@@ -80,7 +85,7 @@
adjustForSetupInPhoneMode(
navButtonsLayoutParams,
navButtonsViewLayoutParams,
- deviceProfile
+ deviceProfile,
)
}
mNavButtonsView.layoutParams = navButtonsViewLayoutParams
@@ -97,7 +102,7 @@
WRAP_CONTENT,
contextualMargin,
contextualMargin,
- Gravity.START
+ Gravity.START,
)
if (imeSwitcher != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 690dec4..23f4f67 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -149,8 +149,9 @@
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewDismissTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewLaunchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
-import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
@@ -459,7 +460,7 @@
protected void onItemClicked(View view) {
if (!mSplitToWorkspaceController.handleSecondAppSelectionForSplit(view)) {
- QuickstepLauncher.super.getItemOnClickListener().onClick(view);
+ super.getItemOnClickListener().onClick(view);
}
}
@@ -687,7 +688,8 @@
}
if (enableExpressiveDismissTaskMotion()) {
- list.add(new TaskViewTouchController<>(this, mTaskViewRecentsTouchContext));
+ list.add(new TaskViewLaunchTouchController<>(this, mTaskViewRecentsTouchContext));
+ list.add(new TaskViewDismissTouchController<>(this, mTaskViewRecentsTouchContext));
} else {
list.add(new TaskViewTouchControllerDeprecated<>(this, mTaskViewRecentsTouchContext));
}
@@ -731,6 +733,9 @@
final boolean ret = super.initDeviceProfile(idp);
mDeviceProfile.isPredictiveBackSwipe =
getApplicationInfo().isOnBackInvokedCallbackEnabled();
+ if (ret) {
+ SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
+ }
return ret;
}
@@ -1160,9 +1165,9 @@
.getHotseatTranslationXForNavBar(this, isBubblesOnLeft);
}
if (isBubbleBarEnabled()
- && mDeviceProfile.shouldAdjustHotseatForBubbleBar(getContext(), hasBubbles())) {
+ && mDeviceProfile.shouldAdjustHotseatForBubbleBar(asContext(), hasBubbles())) {
translationX += (int) mDeviceProfile
- .getHotseatAdjustedTranslation(getContext(), itemInfo.cellX);
+ .getHotseatAdjustedTranslation(asContext(), itemInfo.cellX);
}
return translationX;
}
@@ -1237,6 +1242,7 @@
}
}
+ @NonNull
@Override
public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
ActivityOptionsWrapper activityOptions = mAppTransitionManager.getActivityLaunchOptions(
@@ -1367,12 +1373,6 @@
}
@Override
- protected void onDeviceProfileInitiated() {
- super.onDeviceProfileInitiated();
- SystemUiProxy.INSTANCE.get(this).setLauncherAppIconSize(mDeviceProfile.iconSizePx);
- }
-
- @Override
public void dispatchDeviceProfileChanged() {
super.dispatchDeviceProfileChanged();
Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged",
@@ -1508,4 +1508,9 @@
public void setCanShowAllAppsEducationView(boolean canShowAllAppsEducationView) {
mCanShowAllAppsEducationView = canShowAllAppsEducationView;
}
+
+ @Override
+ public void returnToHomescreen() {
+ getStateManager().goToState(LauncherState.NORMAL);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index d097dba..6e901ee 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -144,6 +144,14 @@
override fun isNonResizeableActivity(lai: LauncherActivityInfo) =
lai.activityInfo.resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE
+ override fun supportsMultiInstance(lai: LauncherActivityInfo) : Boolean {
+ return try {
+ super.supportsMultiInstance(lai) || lai.supportsMultiInstance()
+ } catch (e: Exception) {
+ false
+ }
+ }
+
/**
* Starts an Activity which can be used to set this Launcher as the HOME app, via a consent
* screen. In case the consent screen cannot be shown, or the user does not set current Launcher
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index dae63af..10513c0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -48,8 +48,7 @@
}
@Override
- public <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
- int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
return isToState
? context.getDeviceProfile().allAppsOpenDuration
: context.getDeviceProfile().allAppsCloseDuration;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 932d241..80fc5fa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -17,12 +17,12 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
-import android.content.Context;
import android.graphics.Rect;
import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsView;
/**
@@ -39,7 +39,7 @@
}
@Override
- public int getTransitionDuration(Context launcher, boolean isToState) {
+ public int getTransitionDuration(ActivityContext launcher, boolean isToState) {
return 300;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 5c16a62..15216fe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -29,6 +29,7 @@
import com.android.launcher3.R;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.util.BaseDepthController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -62,10 +63,10 @@
}
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
if (isToState) {
// In gesture modes, overview comes in all the way from the side, so give it more time.
- return DisplayController.getNavigationMode(context).hasGestures
+ return DisplayController.getNavigationMode(context.asContext()).hasGestures
? OVERVIEW_SLIDE_IN_DURATION
: OVERVIEW_POP_IN_DURATION;
} else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 3ae221b..2631fbf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -16,9 +16,8 @@
package com.android.launcher3.uioverrides.states;
-import android.content.Context;
-
import com.android.launcher3.Launcher;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.RecentsView;
@@ -43,11 +42,10 @@
}
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
- boolean isTablet = ((Launcher) context).getDeviceProfile().isTablet;
- if (isToState && isTablet) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
+ if (isToState && context.getDeviceProfile().isTablet) {
return SplitAnimationTimings.TABLET_ENTER_DURATION;
- } else if (isToState && !isTablet) {
+ } else if (isToState) {
return SplitAnimationTimings.PHONE_ENTER_DURATION;
} else {
return SplitAnimationTimings.ABORT_DURATION;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index cbfcda7..05d12c3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -186,7 +186,7 @@
mIsTrackpadSwipe = isTrackpadFourFingerSwipe(ev);
return mIsTrackpadSwipe;
}
- if (DesktopModeStatus.canEnterDesktopMode(mLauncher.getContext())
+ if (DesktopModeStatus.canEnterDesktopMode(mLauncher)
//TODO(b/345296916): replace with dev option once in teamfood
&& Flags.enableQuickswitchDesktopSplitBugfix()
&& mRecentsView.getNonDesktopTaskViewCount() < 1) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
new file mode 100644
index 0000000..99b962b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2025 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.uioverrides.touchcontrollers
+
+import android.content.Context
+import android.view.MotionEvent
+import androidx.dynamicanimation.animation.SpringAnimation
+import com.android.app.animation.Interpolators.DECELERATE
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.Utilities.EDGE_NAV_BAR
+import com.android.launcher3.Utilities.boundToRange
+import com.android.launcher3.Utilities.isRtl
+import com.android.launcher3.Utilities.mapToRange
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.TouchController
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskView
+import kotlin.math.abs
+import kotlin.math.sign
+
+/** Touch controller for handling task view card dismiss swipes */
+class TaskViewDismissTouchController<CONTAINER>(
+ private val container: CONTAINER,
+ private val taskViewRecentsTouchContext: TaskViewRecentsTouchContext,
+) : TouchController, SingleAxisSwipeDetector.Listener where
+CONTAINER : Context,
+CONTAINER : RecentsViewContainer {
+ private val recentsView: RecentsView<*, *> = container.getOverviewPanel()
+ private val detector: SingleAxisSwipeDetector =
+ SingleAxisSwipeDetector(
+ container as Context,
+ this,
+ recentsView.pagedOrientationHandler.upDownSwipeDirection,
+ )
+ private val isRtl = isRtl(container.resources)
+
+ private var taskBeingDragged: TaskView? = null
+ private var springAnimation: SpringAnimation? = null
+ private var dismissLength: Int = 0
+ private var verticalFactor: Int = 0
+ private var initialDisplacement: Float = 0f
+
+ private fun canInterceptTouch(ev: MotionEvent): Boolean =
+ when {
+ // Don't intercept swipes on the nav bar, as user might be trying to go home during a
+ // task dismiss animation.
+ (ev.edgeFlags and EDGE_NAV_BAR) != 0 -> {
+ false
+ }
+
+ // Floating views that a TouchController should not try to intercept touches from.
+ AbstractFloatingView.getTopOpenViewWithType(
+ container,
+ AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT,
+ ) != null -> false
+
+ // Disable swiping if the task overlay is modal.
+ taskViewRecentsTouchContext.isRecentsModal -> {
+ false
+ }
+
+ else -> taskViewRecentsTouchContext.isRecentsInteractive
+ }
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if ((ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL)) {
+ clearState()
+ }
+ if (ev.action == MotionEvent.ACTION_DOWN) {
+ if (!onActionDown(ev)) {
+ return false
+ }
+ }
+
+ onControllerTouchEvent(ev)
+ return detector.isDraggingState && detector.wasInitialTouchPositive()
+ }
+
+ override fun onControllerTouchEvent(ev: MotionEvent?): Boolean = detector.onTouchEvent(ev)
+
+ private fun onActionDown(ev: MotionEvent): Boolean {
+ springAnimation?.cancel()
+ if (!canInterceptTouch(ev)) {
+ return false
+ }
+
+ taskBeingDragged =
+ recentsView.taskViews
+ .firstOrNull {
+ recentsView.isTaskViewVisible(it) && container.dragLayer.isEventOverView(it, ev)
+ }
+ ?.also {
+ dismissLength = recentsView.pagedOrientationHandler.getSecondaryDimension(it)
+ verticalFactor =
+ recentsView.pagedOrientationHandler.secondaryTranslationDirectionFactor
+ }
+
+ detector.setDetectableScrollConditions(
+ recentsView.pagedOrientationHandler.getUpDirection(isRtl),
+ /* ignoreSlop = */ false,
+ )
+
+ return true
+ }
+
+ override fun onDragStart(start: Boolean, startDisplacement: Float) {
+ val taskBeingDragged = taskBeingDragged ?: return
+
+ initialDisplacement =
+ taskBeingDragged.secondaryDismissTranslationProperty.get(taskBeingDragged)
+
+ // Add a tiny bit of translation Z, so that it draws on top of other views. This is relevant
+ // (e.g.) when we dismiss a task by sliding it upward: if there is a row of icons above, we
+ // want the dragged task to stay above all other views.
+ taskBeingDragged.translationZ = 0.1f
+ }
+
+ override fun onDrag(displacement: Float): Boolean {
+ val taskBeingDragged = taskBeingDragged ?: return false
+ val currentDisplacement = displacement + initialDisplacement
+ val boundedDisplacement =
+ boundToRange(abs(currentDisplacement), 0f, dismissLength.toFloat())
+ // When swiping below origin, allow slight undershoot to simulate resisting the movement.
+ val totalDisplacement =
+ if (isDisplacementPositiveDirection(currentDisplacement))
+ boundedDisplacement * sign(currentDisplacement)
+ else
+ mapToRange(
+ boundedDisplacement,
+ 0f,
+ dismissLength.toFloat(),
+ 0f,
+ DISMISS_MAX_UNDERSHOOT,
+ DECELERATE,
+ )
+ taskBeingDragged.secondaryDismissTranslationProperty.setValue(
+ taskBeingDragged,
+ totalDisplacement,
+ )
+ if (taskBeingDragged.isRunningTask && recentsView.enableDrawingLiveTile) {
+ recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
+ remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation.value =
+ totalDisplacement
+ }
+ recentsView.redrawLiveTile()
+ }
+ return true
+ }
+
+ override fun onDragEnd(velocity: Float) {
+ val taskBeingDragged = taskBeingDragged ?: return
+
+ val currentDisplacement =
+ taskBeingDragged.secondaryDismissTranslationProperty.get(taskBeingDragged)
+ if (currentDisplacement == 0f) {
+ clearState()
+ return
+ }
+ val isBeyondDismissThreshold =
+ abs(currentDisplacement) > abs(DISMISS_THRESHOLD_FRACTION * dismissLength)
+ val isFlingingTowardsDismiss = detector.isFling(velocity) && velocity < 0
+ val isFlingingTowardsRestState = detector.isFling(velocity) && velocity > 0
+ val isDismissing =
+ isFlingingTowardsDismiss || (isBeyondDismissThreshold && !isFlingingTowardsRestState)
+ springAnimation =
+ recentsView
+ .createTaskDismissSettlingSpringAnimation(
+ taskBeingDragged,
+ velocity,
+ isDismissing,
+ detector,
+ dismissLength,
+ this::clearState,
+ )
+ .apply {
+ animateToFinalPosition(
+ if (isDismissing) (dismissLength * verticalFactor).toFloat() else 0f
+ )
+ }
+ }
+
+ // Returns if the current task being dragged is towards "positive" (e.g. dismissal).
+ private fun isDisplacementPositiveDirection(displacement: Float): Boolean =
+ sign(displacement) == sign(verticalFactor.toFloat())
+
+ private fun clearState() {
+ detector.finishedScrolling()
+ detector.setDetectableScrollConditions(0, false)
+ taskBeingDragged?.translationZ = 0f
+ taskBeingDragged = null
+ springAnimation = null
+ }
+
+ companion object {
+ private const val DISMISS_THRESHOLD_FRACTION = 0.5f
+ private const val DISMISS_MAX_UNDERSHOOT = 25f
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
new file mode 100644
index 0000000..c740dad
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewLaunchTouchController.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 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.uioverrides.touchcontrollers
+
+import android.content.Context
+import android.graphics.Rect
+import android.view.MotionEvent
+import com.android.app.animation.Interpolators.ZOOM_IN
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.LauncherAnimUtils
+import com.android.launcher3.Utilities.EDGE_NAV_BAR
+import com.android.launcher3.Utilities.boundToRange
+import com.android.launcher3.Utilities.isRtl
+import com.android.launcher3.anim.AnimatorPlaybackController
+import com.android.launcher3.touch.BaseSwipeDetector
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.util.FlingBlockCheck
+import com.android.launcher3.util.TouchController
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskView
+import kotlin.math.abs
+
+/** Touch controller which handles dragging task view cards for launch. */
+class TaskViewLaunchTouchController<CONTAINER>(
+ private val container: CONTAINER,
+ private val taskViewRecentsTouchContext: TaskViewRecentsTouchContext,
+) : TouchController, SingleAxisSwipeDetector.Listener where
+CONTAINER : Context,
+CONTAINER : RecentsViewContainer {
+ private val tempRect = Rect()
+ private val flingBlockCheck = FlingBlockCheck()
+ private val recentsView: RecentsView<*, *> = container.getOverviewPanel()
+ private val detector: SingleAxisSwipeDetector =
+ SingleAxisSwipeDetector(
+ container as Context,
+ this,
+ recentsView.pagedOrientationHandler.upDownSwipeDirection,
+ )
+ private val isRtl = isRtl(container.resources)
+
+ private var taskBeingDragged: TaskView? = null
+ private var launchEndDisplacement: Float = 0f
+ private var playbackController: AnimatorPlaybackController? = null
+ private var verticalFactor: Int = 0
+
+ private fun canTaskLaunchTaskView(taskView: TaskView?) =
+ taskView != null &&
+ taskView === recentsView.currentPageTaskView &&
+ DisplayController.getNavigationMode(container).hasGestures &&
+ (!recentsView.showAsGrid() || taskView.isLargeTile) &&
+ recentsView.isTaskInExpectedScrollPosition(taskView)
+
+ private fun canInterceptTouch(ev: MotionEvent): Boolean =
+ when {
+ // Don't intercept swipes on the nav bar, as user might be trying to go home during a
+ // task dismiss animation.
+ (ev.edgeFlags and EDGE_NAV_BAR) != 0 -> {
+ false
+ }
+
+ // Floating views that a TouchController should not try to intercept touches from.
+ AbstractFloatingView.getTopOpenViewWithType(
+ container,
+ AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT,
+ ) != null -> {
+ false
+ }
+
+ // Disable swiping if the task overlay is modal.
+ taskViewRecentsTouchContext.isRecentsModal -> {
+ false
+ }
+
+ else -> taskViewRecentsTouchContext.isRecentsInteractive
+ }
+
+ override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if (
+ (ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL) &&
+ playbackController == null
+ ) {
+ clearState()
+ }
+ if (ev.action == MotionEvent.ACTION_DOWN) {
+ if (!onActionDown(ev)) {
+ clearState()
+ return false
+ }
+ }
+ onControllerTouchEvent(ev)
+ return detector.isDraggingState && !detector.wasInitialTouchPositive()
+ }
+
+ override fun onControllerTouchEvent(ev: MotionEvent) = detector.onTouchEvent(ev)
+
+ private fun onActionDown(ev: MotionEvent): Boolean {
+ if (!canInterceptTouch(ev)) {
+ return false
+ }
+ taskBeingDragged =
+ recentsView.taskViews
+ .firstOrNull {
+ recentsView.isTaskViewVisible(it) && container.dragLayer.isEventOverView(it, ev)
+ }
+ ?.also {
+ verticalFactor =
+ recentsView.pagedOrientationHandler.secondaryTranslationDirectionFactor
+ }
+ if (!canTaskLaunchTaskView(taskBeingDragged)) {
+ return false
+ }
+ detector.setDetectableScrollConditions(
+ recentsView.pagedOrientationHandler.getDownDirection(isRtl),
+ /* ignoreSlop = */ false,
+ )
+ return true
+ }
+
+ override fun onDragStart(start: Boolean, startDisplacement: Float) {
+ val taskBeingDragged = taskBeingDragged ?: return
+
+ val secondaryLayerDimension: Int =
+ recentsView.pagedOrientationHandler.getSecondaryDimension(container.getDragLayer())
+ val maxDuration = 2L * secondaryLayerDimension
+ recentsView.clearPendingAnimation()
+ val pendingAnimation =
+ recentsView.createTaskLaunchAnimation(taskBeingDragged, maxDuration, ZOOM_IN)
+ // Since the thumbnail is what is filling the screen, based the end displacement on it.
+ taskBeingDragged.getThumbnailBounds(tempRect, /* relativeToDragLayer= */ true)
+ launchEndDisplacement = (secondaryLayerDimension - tempRect.bottom).toFloat()
+ playbackController =
+ pendingAnimation.createPlaybackController()?.apply {
+ taskViewRecentsTouchContext.onUserControlledAnimationCreated(this)
+ dispatchOnStart()
+ }
+ }
+
+ override fun onDrag(displacement: Float): Boolean {
+ playbackController?.setPlayFraction(
+ boundToRange(displacement / launchEndDisplacement, 0f, 1f)
+ )
+ return true
+ }
+
+ override fun onDragEnd(velocity: Float) {
+ val playbackController = playbackController ?: return
+
+ val isBeyondLaunchThreshold =
+ abs(playbackController.progressFraction) > abs(LAUNCH_THRESHOLD_FRACTION)
+ val isFlingingTowardsLaunch = detector.isFling(velocity) && velocity > 0
+ val isFlingingTowardsRestState = detector.isFling(velocity) && velocity < 0
+ val isLaunching =
+ isFlingingTowardsLaunch || (isBeyondLaunchThreshold && !isFlingingTowardsRestState)
+
+ val progress = playbackController.progressFraction
+ var animationDuration =
+ BaseSwipeDetector.calculateDuration(
+ velocity,
+ if (isLaunching) (1 - progress) else progress,
+ )
+ if (detector.isFling(velocity) && flingBlockCheck.isBlocked && !isLaunching) {
+ animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity).toLong()
+ }
+
+ playbackController.setEndAction(this::clearState)
+ playbackController.startWithVelocity(
+ container,
+ isLaunching,
+ velocity,
+ launchEndDisplacement,
+ animationDuration,
+ )
+ }
+
+ private fun clearState() {
+ detector.finishedScrolling()
+ detector.setDetectableScrollConditions(0, false)
+ taskBeingDragged = null
+ playbackController = null
+ }
+
+ companion object {
+ private const val LAUNCH_THRESHOLD_FRACTION: Float = 0.5f
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.kt
deleted file mode 100644
index c996f34..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.kt
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2025 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.uioverrides.touchcontrollers
-
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.content.Context
-import android.graphics.Rect
-import android.os.VibrationEffect
-import android.view.MotionEvent
-import android.view.animation.Interpolator
-import com.android.app.animation.Interpolators
-import com.android.launcher3.AbstractFloatingView
-import com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS
-import com.android.launcher3.LauncherAnimUtils.blockedFlingDurationFactor
-import com.android.launcher3.R
-import com.android.launcher3.Utilities
-import com.android.launcher3.anim.AnimatorPlaybackController
-import com.android.launcher3.anim.PendingAnimation
-import com.android.launcher3.touch.BaseSwipeDetector
-import com.android.launcher3.touch.SingleAxisSwipeDetector
-import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.FlingBlockCheck
-import com.android.launcher3.util.TouchController
-import com.android.launcher3.util.VibratorWrapper
-import com.android.quickstep.util.VibrationConstants
-import com.android.quickstep.views.RecentsView
-import com.android.quickstep.views.RecentsViewContainer
-import com.android.quickstep.views.TaskView
-import kotlin.math.abs
-
-/** Touch controller for handling task view card swipes */
-class TaskViewTouchController<CONTAINER>(
- private val container: CONTAINER,
- private val taskViewRecentsTouchContext: TaskViewRecentsTouchContext,
-) : AnimatorListenerAdapter(), TouchController, SingleAxisSwipeDetector.Listener where
-CONTAINER : Context,
-CONTAINER : RecentsViewContainer {
- private val recentsView: RecentsView<*, *> = container.getOverviewPanel()
- private val detector: SingleAxisSwipeDetector =
- SingleAxisSwipeDetector(
- container as Context,
- this,
- recentsView.pagedOrientationHandler.upDownSwipeDirection,
- )
- private val tempRect = Rect()
- private val isRtl = Utilities.isRtl(container.resources)
- private val flingBlockCheck = FlingBlockCheck()
-
- private var currentAnimation: AnimatorPlaybackController? = null
- private var currentAnimationIsGoingUp = false
- private var allowGoingUp = false
- private var allowGoingDown = false
- private var noIntercept = false
- private var displacementShift = 0f
- private var progressMultiplier = 0f
- private var endDisplacement = 0f
- private var draggingEnabled = true
- private var overrideVelocity: Float? = null
- private var taskBeingDragged: TaskView? = null
- private var isDismissHapticRunning = false
-
- private fun canInterceptTouch(ev: MotionEvent): Boolean {
- val currentAnimation = currentAnimation
- return when {
- (ev.edgeFlags and Utilities.EDGE_NAV_BAR) != 0 -> {
- // Don't intercept swipes on the nav bar, as user might be trying to go home
- // during a task dismiss animation.
- currentAnimation?.animationPlayer?.end()
- false
- }
- currentAnimation != null -> {
- currentAnimation.forceFinishIfCloseToEnd()
- true
- }
- AbstractFloatingView.getTopOpenViewWithType(
- container,
- AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT,
- ) != null -> false
- else -> taskViewRecentsTouchContext.isRecentsInteractive
- }
- }
-
- override fun onAnimationCancel(animation: Animator) {
- if (animation === currentAnimation?.target) {
- clearState()
- }
- }
-
- override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
- if (
- (ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL) &&
- currentAnimation == null
- ) {
- clearState()
- }
- if (ev.action == MotionEvent.ACTION_DOWN) {
- // Disable swiping up and down if the task overlay is modal.
- if (taskViewRecentsTouchContext.isRecentsModal) {
- noIntercept = true
- return false
- }
- noIntercept = !canInterceptTouch(ev)
- if (noIntercept) {
- return false
- }
- // Now figure out which direction scroll events the controller will start
- // calling the callbacks.
- var directionsToDetectScroll = 0
- var ignoreSlopWhenSettling = false
- if (currentAnimation != null) {
- directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH
- ignoreSlopWhenSettling = true
- } else {
- taskBeingDragged = null
- recentsView.taskViews.forEach { taskView ->
- if (
- recentsView.isTaskViewVisible(taskView) &&
- container.dragLayer.isEventOverView(taskView, ev)
- ) {
- taskBeingDragged = taskView
- val upDirection = recentsView.pagedOrientationHandler.getUpDirection(isRtl)
-
- // The task can be dragged up to dismiss it
- allowGoingUp = true
-
- // The task can be dragged down to open it if:
- // - It's the current page
- // - We support gestures to enter overview
- // - It's the focused task if in grid view
- // - The task is snapped
- allowGoingDown =
- taskView === recentsView.currentPageTaskView &&
- DisplayController.getNavigationMode(container).hasGestures &&
- (!recentsView.showAsGrid() || taskView.isLargeTile) &&
- recentsView.isTaskInExpectedScrollPosition(taskView)
-
- directionsToDetectScroll =
- if (allowGoingDown) SingleAxisSwipeDetector.DIRECTION_BOTH
- else upDirection
- return@forEach
- }
- }
- if (taskBeingDragged == null) {
- noIntercept = true
- return false
- }
- }
- detector.setDetectableScrollConditions(directionsToDetectScroll, ignoreSlopWhenSettling)
- }
- if (noIntercept) {
- return false
- }
- onControllerTouchEvent(ev)
- return detector.isDraggingOrSettling
- }
-
- override fun onControllerTouchEvent(ev: MotionEvent): Boolean = detector.onTouchEvent(ev)
-
- private fun reInitAnimationController(goingUp: Boolean) {
- if (currentAnimation != null && currentAnimationIsGoingUp == goingUp) {
- // No need to init
- return
- }
- if ((goingUp && !allowGoingUp) || (!goingUp && !allowGoingDown)) {
- // Trying to re-init in an unsupported direction.
- return
- }
- val taskBeingDragged = taskBeingDragged ?: return
- currentAnimation?.setPlayFraction(0f)
- currentAnimation?.target?.removeListener(this)
- currentAnimation?.dispatchOnCancel()
-
- val orientationHandler = recentsView.pagedOrientationHandler
- currentAnimationIsGoingUp = goingUp
- val dl = container.dragLayer
- val secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl)
- val maxDuration = 2L * secondaryLayerDimension
- val verticalFactor = orientationHandler.getTaskDragDisplacementFactor(isRtl)
- val secondaryTaskDimension = orientationHandler.getSecondaryDimension(taskBeingDragged)
- // The interpolator controlling the most prominent visual movement. We use this to determine
- // whether we passed SUCCESS_TRANSITION_PROGRESS.
- val currentInterpolator: Interpolator
- val pa: PendingAnimation
- if (goingUp) {
- currentInterpolator = Interpolators.LINEAR
- pa = PendingAnimation(maxDuration)
- recentsView.createTaskDismissAnimation(
- pa,
- taskBeingDragged,
- true, /* animateTaskView */
- true, /* removeTask */
- maxDuration,
- false, /* dismissingForSplitSelection*/
- )
-
- endDisplacement = -secondaryTaskDimension.toFloat()
- } else {
- currentInterpolator = Interpolators.ZOOM_IN
- pa =
- recentsView.createTaskLaunchAnimation(
- taskBeingDragged,
- maxDuration,
- currentInterpolator,
- )
-
- // Since the thumbnail is what is filling the screen, based the end displacement on it.
- taskBeingDragged.getThumbnailBounds(tempRect, /* relativeToDragLayer= */ true)
- endDisplacement = (secondaryLayerDimension - tempRect.bottom).toFloat()
- }
- endDisplacement *= verticalFactor.toFloat()
- currentAnimation =
- pa.createPlaybackController().apply {
- // Setting this interpolator doesn't affect the visual motion, but is used to
- // determine whether we successfully reached the target state in onDragEnd().
- target.interpolator = currentInterpolator
- taskViewRecentsTouchContext.onUserControlledAnimationCreated(this)
- target.addListener(this@TaskViewTouchController)
- dispatchOnStart()
- }
- progressMultiplier = 1 / endDisplacement
- }
-
- override fun onDragStart(start: Boolean, startDisplacement: Float) {
- if (!draggingEnabled) return
- val currentAnimation = currentAnimation
-
- val orientationHandler = recentsView.pagedOrientationHandler
- if (currentAnimation == null) {
- reInitAnimationController(orientationHandler.isGoingUp(startDisplacement, isRtl))
- displacementShift = 0f
- } else {
- displacementShift = currentAnimation.progressFraction / progressMultiplier
- currentAnimation.pause()
- }
- flingBlockCheck.unblockFling()
- overrideVelocity = null
- }
-
- override fun onDrag(displacement: Float): Boolean {
- if (!draggingEnabled) return true
- val taskBeingDragged = taskBeingDragged ?: return true
- val currentAnimation = currentAnimation ?: return true
-
- val orientationHandler = recentsView.pagedOrientationHandler
- val totalDisplacement = displacement + displacementShift
- val isGoingUp =
- if (totalDisplacement == 0f) currentAnimationIsGoingUp
- else orientationHandler.isGoingUp(totalDisplacement, isRtl)
- if (isGoingUp != currentAnimationIsGoingUp) {
- reInitAnimationController(isGoingUp)
- flingBlockCheck.blockFling()
- } else {
- flingBlockCheck.onEvent()
- }
-
- if (isGoingUp) {
- if (currentAnimation.progressFraction < ANIMATION_PROGRESS_FRACTION_MIDPOINT) {
- // Halve the value when dismissing, as we are animating the drag across the full
- // length for only the first half of the progress
- currentAnimation.setPlayFraction(
- Utilities.boundToRange(totalDisplacement * progressMultiplier / 2, 0f, 1f)
- )
- } else {
- // Set mOverrideVelocity to control task dismiss velocity in onDragEnd
- var velocityDimenId = R.dimen.default_task_dismiss_drag_velocity
- if (recentsView.showAsGrid()) {
- velocityDimenId =
- if (taskBeingDragged.isLargeTile) {
- R.dimen.default_task_dismiss_drag_velocity_grid_focus_task
- } else {
- R.dimen.default_task_dismiss_drag_velocity_grid
- }
- }
- overrideVelocity = -taskBeingDragged.resources.getDimension(velocityDimenId)
-
- // Once halfway through task dismissal interpolation, switch from reversible
- // dragging-task animation to playing the remaining task translation animations,
- // while this is in progress disable dragging.
- draggingEnabled = false
- }
- } else {
- currentAnimation.setPlayFraction(
- Utilities.boundToRange(totalDisplacement * progressMultiplier, 0f, 1f)
- )
- }
-
- return true
- }
-
- override fun onDragEnd(velocity: Float) {
- val taskBeingDragged = taskBeingDragged ?: return
- val currentAnimation = currentAnimation ?: return
-
- // Limit velocity, as very large scalar values make animations play too quickly
- val maxTaskDismissDragVelocity =
- taskBeingDragged.resources.getDimension(R.dimen.max_task_dismiss_drag_velocity)
- val endVelocity =
- Utilities.boundToRange(
- overrideVelocity ?: velocity,
- -maxTaskDismissDragVelocity,
- maxTaskDismissDragVelocity,
- )
- overrideVelocity = null
-
- var fling = draggingEnabled && detector.isFling(endVelocity)
- val goingToEnd: Boolean
- val blockedFling = fling && flingBlockCheck.isBlocked
- if (blockedFling) {
- fling = false
- }
- val orientationHandler = recentsView.pagedOrientationHandler
- val goingUp = orientationHandler.isGoingUp(endVelocity, isRtl)
- val progress = currentAnimation.progressFraction
- val interpolatedProgress = currentAnimation.interpolatedProgress
- goingToEnd =
- if (fling) {
- goingUp == currentAnimationIsGoingUp
- } else {
- interpolatedProgress > SUCCESS_TRANSITION_PROGRESS
- }
- var animationDuration =
- BaseSwipeDetector.calculateDuration(
- endVelocity,
- if (goingToEnd) (1 - progress) else progress,
- )
- if (blockedFling && !goingToEnd) {
- animationDuration *= blockedFlingDurationFactor(endVelocity).toLong()
- }
- // Due to very high or low velocity dismissals, animation durations can be inconsistently
- // long or short. Bound the duration for animation of task translations for a more
- // standardized feel.
- animationDuration =
- Utilities.boundToRange(
- animationDuration,
- MIN_TASK_DISMISS_ANIMATION_DURATION,
- MAX_TASK_DISMISS_ANIMATION_DURATION,
- )
-
- currentAnimation.setEndAction { this.clearState() }
- currentAnimation.startWithVelocity(
- container,
- goingToEnd,
- abs(endVelocity.toDouble()).toFloat(),
- endDisplacement,
- animationDuration,
- )
- if (goingUp && goingToEnd && !isDismissHapticRunning) {
- VibratorWrapper.INSTANCE.get(container)
- .vibrate(
- TASK_DISMISS_VIBRATION_PRIMITIVE,
- TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE,
- TASK_DISMISS_VIBRATION_FALLBACK,
- )
- isDismissHapticRunning = true
- }
-
- draggingEnabled = true
- }
-
- private fun clearState() {
- detector.finishedScrolling()
- detector.setDetectableScrollConditions(0, false)
- draggingEnabled = true
- taskBeingDragged = null
- currentAnimation = null
- isDismissHapticRunning = false
- }
-
- companion object {
- private const val ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f
- private const val MIN_TASK_DISMISS_ANIMATION_DURATION: Long = 300
- private const val MAX_TASK_DISMISS_ANIMATION_DURATION: Long = 600
-
- private const val TASK_DISMISS_VIBRATION_PRIMITIVE: Int =
- VibrationEffect.Composition.PRIMITIVE_TICK
- private const val TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE: Float = 1f
- private val TASK_DISMISS_VIBRATION_FALLBACK: VibrationEffect =
- VibrationConstants.EFFECT_TEXTURE_TICK
- }
-}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5f8b89a..f46f9ae 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -120,7 +120,6 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.taskbar.TaskbarThresholdUtils;
@@ -1491,7 +1490,7 @@
startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
- private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
+ private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTaskView) {
if (mDp == null || !mDp.isGestureMode) {
// We probably never received an animation controller, skip logging.
return;
@@ -1509,9 +1508,9 @@
case NEW_TASK:
events.add(mLogDirectionUpOrLeft ? LAUNCHER_QUICKSWITCH_LEFT
: LAUNCHER_QUICKSWITCH_RIGHT);
- if (targetTask != null && DesktopModeStatus.canEnterDesktopMode(mContext)
+ if (targetTaskView != null && DesktopModeStatus.canEnterDesktopMode(mContext)
&& DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH.isTrue()) {
- if (targetTask.getType() == TaskViewType.DESKTOP) {
+ if (targetTaskView.getType() == TaskViewType.DESKTOP) {
events.add(LAUNCHER_QUICKSWITCH_ENTER_DESKTOP_MODE);
} else if (mPreviousTaskViewType == TaskViewType.DESKTOP) {
events.add(LAUNCHER_QUICKSWITCH_EXIT_DESKTOP_MODE);
@@ -1528,9 +1527,8 @@
.withInputType(mGestureState.isTrackpadGesture()
? SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TRACKPAD
: SysUiStatsLog.LAUNCHER_UICHANGED__INPUT_TYPE__TOUCH);
- ItemInfo itemInfo;
- if (targetTask != null && (itemInfo = targetTask.getFirstItemInfo()) != null) {
- logger.withItemInfo(itemInfo);
+ if (targetTaskView != null) {
+ logger.withItemInfo(targetTaskView.getItemInfo());
}
int pageIndex = endTarget == LAST_TASK || mRecentsView == null
@@ -1711,7 +1709,7 @@
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
- getRemoteTaskViewSimulators());
+ mRemoteTargetHandles);
}
} else {
AnimatorSet animatorSet = new AnimatorSet();
@@ -1755,7 +1753,7 @@
mRecentsView.onPrepareGestureEndAnimation(
mGestureState.isHandlingAtomicEvent() ? null : animatorSet,
mGestureState.getEndTarget(),
- getRemoteTaskViewSimulators());
+ mRemoteTargetHandles);
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
diff --git a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
index 241e16d..1345e0b 100644
--- a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
+++ b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
@@ -18,6 +18,7 @@
import android.os.RemoteException
import android.util.Log
+import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.Executors
import com.android.wm.shell.shared.IHomeTransitionListener.Stub
@@ -41,10 +42,13 @@
transitions?.setHomeTransitionListener(
object : Stub() {
override fun onHomeVisibilityChanged(isVisible: Boolean) {
- Executors.MAIN_EXECUTOR.execute {
- isHomeVisible = isVisible
- listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
- }
+ Utilities.postAsyncCallback(
+ Executors.MAIN_EXECUTOR.handler,
+ {
+ isHomeVisible = isVisible
+ listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
+ },
+ )
}
}
)
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 5e8ea37..fca67c3 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -247,7 +248,6 @@
@Override
public void returnToHomescreen() {
- super.returnToHomescreen();
// TODO(b/137318995) This should go home, but doing so removes freeform windows
}
@@ -261,6 +261,7 @@
}
}
+ @NonNull
@Override
public ActivityOptionsWrapper getActivityLaunchOptions(final View v, @Nullable ItemInfo item) {
if (!(v instanceof TaskView)) {
@@ -371,6 +372,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setWallpaperDependentTheme(this);
mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
@@ -431,7 +433,6 @@
*/
private void initDeviceProfile() {
mDeviceProfile = createDeviceProfile();
- onDeviceProfileInitiated();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index e1e962a..090ccdc 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -114,7 +114,6 @@
private final RotationTouchHelper mRotationTouchHelper;
private final TaskStackChangeListener mPipListener;
- private final DaggerSingletonTracker mLifeCycle;
// Cache for better performance since it doesn't change at runtime.
private final boolean mCanImeRenderGesturalNavButtons =
InputMethodService.canImeRenderGesturalNavButtons();
@@ -152,16 +151,15 @@
mExclusionManager = exclusionManager;
mContextualSearchStateManager = contextualSearchStateManager;
mRotationTouchHelper = rotationTouchHelper;
- mLifeCycle = lifeCycle;
mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
// Register for exclusion updates
- mLifeCycle.addCloseable(this::unregisterExclusionListener);
+ lifeCycle.addCloseable(this::unregisterExclusionListener);
// Register for display changes changes
mDisplayController.addChangeListener(this);
onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL);
- mLifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(this));
+ lifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(this));
if (mIsOneHandedModeSupported) {
Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED);
@@ -169,7 +167,7 @@
enabled -> mIsOneHandedModeEnabled = enabled;
settingsCache.register(oneHandedUri, onChangeListener);
mIsOneHandedModeEnabled = settingsCache.getValue(oneHandedUri);
- mLifeCycle.addCloseable(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
+ lifeCycle.addCloseable(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
} else {
mIsOneHandedModeEnabled = false;
}
@@ -180,7 +178,7 @@
enabled -> mIsSwipeToNotificationEnabled = enabled;
settingsCache.register(swipeBottomNotificationUri, onChangeListener);
mIsSwipeToNotificationEnabled = settingsCache.getValue(swipeBottomNotificationUri);
- mLifeCycle.addCloseable(
+ lifeCycle.addCloseable(
() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
@@ -188,7 +186,7 @@
if (!mIsUserSetupComplete) {
SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e;
settingsCache.register(setupCompleteUri, userSetupChangeListener);
- mLifeCycle.addCloseable(
+ lifeCycle.addCloseable(
() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
}
@@ -210,15 +208,19 @@
}
};
TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
- mLifeCycle.addCloseable(() ->
+ lifeCycle.addCloseable(() ->
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
}
/**
* Adds a listener for the nav mode change, guaranteed to be called after the device state's
* mode has changed.
+ *
+ * @return Added {@link DisplayInfoChangeListener} so that caller is
+ * responsible for removing the listener from {@link DisplayController} to avoid memory leak.
*/
- public void addNavigationModeChangedCallback(Runnable callback) {
+ public DisplayController.DisplayInfoChangeListener addNavigationModeChangedCallback(
+ Runnable callback) {
DisplayController.DisplayInfoChangeListener listener = (context, info, flags) -> {
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
callback.run();
@@ -226,7 +228,16 @@
};
mDisplayController.addChangeListener(listener);
callback.run();
- mLifeCycle.addCloseable(() -> mDisplayController.removeChangeListener(listener));
+ return listener;
+ }
+
+ /**
+ * Remove the {DisplayController.DisplayInfoChangeListener} added from
+ * {@link #addNavigationModeChangedCallback} when {@link TouchInteractionService} is destroyed.
+ */
+ public void removeDisplayInfoChangeListener(
+ DisplayController.DisplayInfoChangeListener listener) {
+ mDisplayController.removeChangeListener(listener);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index dc5d59f..87b58e6 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -41,6 +42,7 @@
import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.recents.data.RecentTasksDataSource;
@@ -63,6 +65,8 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
+import javax.inject.Provider;
+
/**
* Singleton class to load and manage recents model.
*/
@@ -86,15 +90,19 @@
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
private final ComponentCallbacks mCallbacks;
- private final ThemeManager mThemeManager;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final SafeCloseable mIconChangeCloseable;
+ private final LockedUserState mLockedUserState;
+ private final Provider<ThemeManager> mThemeManagerProvider;
+ private final Runnable mUnlockCallback;
+
private RecentsModel(Context context) {
this(context, new IconProvider(context));
}
+ @SuppressLint("VisibleForTests")
private RecentsModel(Context context, IconProvider iconProvider) {
this(context,
new RecentTasksList(
@@ -107,14 +115,16 @@
new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
iconProvider,
TaskStackChangeListeners.getInstance(),
- ThemeManager.INSTANCE.get(context));
+ LockedUserState.get(context),
+ () -> ThemeManager.INSTANCE.get(context));
}
@VisibleForTesting
RecentsModel(Context context, RecentTasksList taskList, TaskIconCache iconCache,
TaskThumbnailCache thumbnailCache, IconProvider iconProvider,
TaskStackChangeListeners taskStackChangeListeners,
- ThemeManager themeManager) {
+ LockedUserState lockedUserState,
+ Provider<ThemeManager> themeManagerProvider) {
mContext = context;
mTaskList = taskList;
mIconCache = iconCache;
@@ -140,8 +150,11 @@
mTaskStackChangeListeners.registerTaskStackListener(this);
mIconChangeCloseable = iconProvider.registerIconChangeListener(
this::onAppIconChanged, MAIN_EXECUTOR.getHandler());
- mThemeManager = themeManager;
- themeManager.addChangeListener(this);
+
+ mLockedUserState = lockedUserState;
+ mThemeManagerProvider = themeManagerProvider;
+ mUnlockCallback = () -> mThemeManagerProvider.get().addChangeListener(this);
+ lockedUserState.runOnUserUnlocked(mUnlockCallback);
}
public TaskIconCache getIconCache() {
@@ -402,7 +415,12 @@
mIconCache.removeTaskVisualsChangeListener();
mTaskStackChangeListeners.unregisterTaskStackListener(this);
mIconChangeCloseable.close();
- mThemeManager.removeChangeListener(this);
+
+ if (mLockedUserState.isUserUnlocked()) {
+ mThemeManagerProvider.get().removeChangeListener(this);
+ } else {
+ mLockedUserState.removeOnUserUnlockedRunnable(mUnlockCallback);
+ }
}
private boolean isCachePreloadingEnabled() {
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 2dbd5e9..7990aae 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -136,7 +136,7 @@
public SplitSelectSystemShortcut(RecentsViewContainer container,
TaskContainer taskContainer, TaskView taskView,
SplitPositionOption option) {
- super(option.iconResId, option.textResId, container, taskView.getFirstItemInfo(),
+ super(option.iconResId, option.textResId, container, taskContainer.getItemInfo(),
taskView);
mTaskContainer = taskContainer;
mSplitPositionOption = option;
@@ -164,8 +164,7 @@
public SaveAppPairSystemShortcut(RecentsViewContainer container, GroupedTaskView taskView,
int iconResId) {
- super(iconResId, R.string.save_app_pair, container, taskView.getFirstItemInfo(),
- taskView);
+ super(iconResId, R.string.save_app_pair, container, taskView.getItemInfo(), taskView);
mTaskView = taskView;
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 51e59ff..2df4a45 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -302,80 +302,65 @@
@BinderThread
@Override
public void onDisplayAddSystemDecorations(int displayId) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(taskbarManager ->
- taskbarManager.onDisplayAddSystemDecorations(displayId))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.onDisplayAddSystemDecorations(displayId));
}
@BinderThread
@Override
public void onDisplayRemoved(int displayId) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(
- taskbarManager -> taskbarManager.onDisplayRemoved(displayId))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.onDisplayRemoved(displayId));
}
@BinderThread
@Override
public void onDisplayRemoveSystemDecorations(int displayId) {
- // TODO(b/391786915): Replace all
- // `executeForTouchInteractionService(executeForTaskbarManager())` with just
- // `executeForTaskbarManager` directly (since `tis` is unused).
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(taskbarManager -> taskbarManager
- .onDisplayRemoveSystemDecorations(displayId))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.onDisplayRemoveSystemDecorations(displayId));
}
@BinderThread
@Override
public void updateWallpaperVisibility(int displayId, boolean visible) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(
- taskbarManager -> taskbarManager.setWallpaperVisible(displayId,
- visible))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.setWallpaperVisible(displayId, visible));
}
@BinderThread
@Override
public void checkNavBarModes(int displayId) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
- executeForTaskbarManager(
- taskbarManager -> taskbarManager.checkNavBarModes(displayId))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.checkNavBarModes(displayId));
}
@BinderThread
@Override
public void finishBarAnimations(int displayId) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(
- taskbarManager -> taskbarManager.finishBarAnimations(displayId))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.finishBarAnimations(displayId));
}
@BinderThread
@Override
public void touchAutoDim(int displayId, boolean reset) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(
- taskbarManager -> taskbarManager.touchAutoDim(displayId, reset))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.touchAutoDim(displayId, reset));
}
@BinderThread
@Override
public void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode,
boolean animate) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
- tis -> executeForTaskbarManager(
- taskbarManager -> taskbarManager.transitionTo(displayId, barMode,
- animate))));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.transitionTo(displayId, barMode, animate));
}
@BinderThread
@Override
public void appTransitionPending(boolean pending) {
- MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
- executeForTaskbarManager(
- taskbarManager -> taskbarManager.appTransitionPending(pending))
- ));
+ executeForTaskbarManager(taskbarManager ->
+ taskbarManager.appTransitionPending(pending));
}
@Override
@@ -576,6 +561,8 @@
private DesktopAppLaunchTransitionManager mDesktopAppLaunchTransitionManager;
+ private DisplayController.DisplayInfoChangeListener mDisplayInfoChangeListener;
+
@Override
public void onCreate() {
super.onCreate();
@@ -605,7 +592,8 @@
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
LockedUserState.get(this).runOnUserUnlocked(mUserUnlockedRunnable);
- mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
+ mDisplayInfoChangeListener =
+ mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
}
@@ -757,7 +745,7 @@
mDesktopAppLaunchTransitionManager.unregisterTransitions();
}
mDesktopAppLaunchTransitionManager = null;
-
+ mDeviceState.removeDisplayInfoChangeListener(mDisplayInfoChangeListener);
LockedUserState.get(this).removeOnUserUnlockedRunnable(mUserUnlockedRunnable);
ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
super.onDestroy();
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 44fdaec..b4b80c5 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -128,8 +128,7 @@
state.getScrimColor(mRecentsViewContainer.asContext()),
config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
if (isSplitSelectionState(state)) {
- int duration =
- state.getTransitionDuration(mRecentsViewContainer.asContext(), true);
+ int duration = state.getTransitionDuration(mRecentsViewContainer, true);
// TODO (b/246851887): Pass in setter as a NO_ANIM PendingAnimation instead
PendingAnimation pa = new PendingAnimation(duration);
mRecentsView.createSplitSelectInitAnimation(pa, duration);
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 8d010e2..f426bf5 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -44,11 +44,11 @@
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.GestureState;
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SingleTask;
import com.android.quickstep.util.SplitSelectStateController;
-import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@@ -129,8 +129,8 @@
@Override
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
- TaskViewSimulator[] taskViewSimulators) {
- super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulators);
+ RemoteTargetHandle[] remoteTargetHandles) {
+ super.onPrepareGestureEndAnimation(animatorSet, endTarget, remoteTargetHandles);
if (mHomeTask != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskViewByTaskId(mHomeTask.key.id);
if (tv != null) {
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
index 7e5afc3..5d4f1db 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
@@ -21,8 +21,9 @@
import android.util.AttributeSet;
import com.android.launcher3.statemanager.StatefulContainer;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewDismissTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewLaunchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
-import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -54,10 +55,18 @@
@Override
public void recreateControllers() {
- mControllers = new TouchController[]{
- enableExpressiveDismissTaskMotion() ? new TaskViewTouchController<>(mContainer,
- mTaskViewRecentsTouchContext) : new TaskViewTouchControllerDeprecated<>(
- mContainer, mTaskViewRecentsTouchContext),
- new FallbackNavBarTouchController(mContainer)};
+ mControllers = enableExpressiveDismissTaskMotion()
+ ? new TouchController[]{
+ new TaskViewLaunchTouchController<>(mContainer,
+ mTaskViewRecentsTouchContext),
+ new TaskViewDismissTouchController<>(mContainer,
+ mTaskViewRecentsTouchContext),
+ new FallbackNavBarTouchController(mContainer)
+ }
+ : new TouchController[]{
+ new TaskViewTouchControllerDeprecated<>(mContainer,
+ mTaskViewRecentsTouchContext),
+ new FallbackNavBarTouchController(mContainer)
+ };
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index c2e7536..f27b60c 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -27,6 +27,7 @@
import com.android.launcher3.R;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsViewContainer;
/**
@@ -92,7 +93,7 @@
}
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
return 250;
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 3082dc6..07288d8 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -390,10 +390,6 @@
return systemUiController
}
- override fun getContext(): Context {
- return this
- }
-
override fun getScrimView(): ScrimView? {
return scrimView
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 42e8694..be47df9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -24,11 +24,10 @@
import android.graphics.PointF;
import android.view.MotionEvent;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
@@ -80,7 +79,7 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null, TAG);
- BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
+ ActivityContext activity = ActivityContext.lookupContext(mContext);
int state = (mGestureState != null && mGestureState.getEndTarget() != null)
? mGestureState.getEndTarget().containerType
: LAUNCHER_STATE_HOME;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index b2a30ca..594c99a 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -425,6 +425,22 @@
case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END:
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
break;
+ case LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN:
+ InteractionJankMonitorWrapper.begin(
+ view,
+ Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND);
+ break;
+ case LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END:
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND);
+ break;
+ case LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN:
+ InteractionJankMonitorWrapper.begin(
+ view,
+ Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK);
+ break;
+ case LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END:
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK);
+ break;
default:
break;
}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index 88ef0a8..e72ccbf 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -306,6 +306,10 @@
if (isRtl) SingleAxisSwipeDetector.DIRECTION_NEGATIVE
else SingleAxisSwipeDetector.DIRECTION_POSITIVE
+ override fun getDownDirection(isRtl: Boolean): Int =
+ if (isRtl) SingleAxisSwipeDetector.DIRECTION_POSITIVE
+ else SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+
override fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean =
if (isRtl) displacement < 0 else displacement > 0
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index c0b697d..c1e1c2b 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -310,6 +310,12 @@
}
@Override
+ public int getDownDirection(boolean isRtl) {
+ // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+ return SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+ }
+
+ @Override
public boolean isGoingUp(float displacement, boolean isRtl) {
// Ignore rtl since it only affects X value displacement, Y displacement doesn't change
return displacement < 0;
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index b8d0412..78f9a0a 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -332,6 +332,9 @@
/** @return Given [.getUpDownSwipeDirection], whether POSITIVE or NEGATIVE is up. */
fun getUpDirection(isRtl: Boolean): Int
+ /** @return Given [.getUpDownSwipeDirection], whether POSITIVE or NEGATIVE is down. */
+ fun getDownDirection(isRtl: Boolean): Int
+
/** @return Whether the displacement is going towards the top of the screen. */
fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index bc91911..3fb4f54 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -351,6 +351,10 @@
if (isRtl) SingleAxisSwipeDetector.DIRECTION_POSITIVE
else SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+ override fun getDownDirection(isRtl: Boolean): Int =
+ if (isRtl) SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+ else SingleAxisSwipeDetector.DIRECTION_POSITIVE
+
override fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean =
if (isRtl) displacement > 0 else displacement < 0
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 02f48e6..1f428f3 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -28,6 +28,7 @@
import com.android.quickstep.recents.data.TasksRepository
import com.android.quickstep.recents.domain.usecase.GetSysUiStatusNavFlagsUseCase
import com.android.quickstep.recents.domain.usecase.GetTaskUseCase
+import com.android.quickstep.recents.domain.usecase.OrganizeDesktopTasksUseCase
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
import com.android.quickstep.recents.viewmodel.RecentsViewData
@@ -201,6 +202,7 @@
rotationStateRepository = inject(),
tasksRepository = inject(),
)
+ OrganizeDesktopTasksUseCase::class.java -> OrganizeDesktopTasksUseCase()
SplashAlphaUseCase::class.java ->
SplashAlphaUseCase(
recentsViewData = inject(),
diff --git a/quickstep/src/com/android/quickstep/recents/domain/model/DesktopTaskBoundsData.kt b/quickstep/src/com/android/quickstep/recents/domain/model/DesktopTaskBoundsData.kt
new file mode 100644
index 0000000..a7f102c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/domain/model/DesktopTaskBoundsData.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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.recents.domain.model
+
+import android.graphics.Rect
+
+data class DesktopTaskBoundsData(val taskId: Int, val bounds: Rect)
diff --git a/quickstep/src/com/android/quickstep/recents/domain/usecase/OrganizeDesktopTasksUseCase.kt b/quickstep/src/com/android/quickstep/recents/domain/usecase/OrganizeDesktopTasksUseCase.kt
new file mode 100644
index 0000000..3e7d142
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/domain/usecase/OrganizeDesktopTasksUseCase.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2025 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.recents.domain.usecase
+
+import android.graphics.Rect
+import android.util.Size
+import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
+import kotlin.math.ceil
+import kotlin.math.sqrt
+
+/**
+ * This usecase is responsible for organizing desktop windows in a non-overlapping way. Note: this
+ * is currently a placeholder implementation.
+ */
+class OrganizeDesktopTasksUseCase {
+ fun run(
+ desktopSize: Size,
+ taskBounds: List<DesktopTaskBoundsData>,
+ ): List<DesktopTaskBoundsData> {
+ return getRects(desktopSize, taskBounds.size).zip(taskBounds) { rect, task ->
+ shrinkRect(rect, 0.8f)
+ DesktopTaskBoundsData(task.taskId, fitRect(task.bounds, rect))
+ }
+ }
+
+ private fun shrinkRect(bounds: Rect, fraction: Float) {
+ val xMargin = (bounds.width() * ((1.0f - fraction) / 2.0f)).toInt()
+ val yMargin = (bounds.height() * ((1.0f - fraction) / 2.0f)).toInt()
+ bounds.inset(xMargin, yMargin, xMargin, yMargin)
+ }
+
+ /** Generates `tasks` number of non-overlapping rects that fit into `desktopSize`. */
+ private fun getRects(desktopSize: Size, tasks: Int): List<Rect> {
+ val (xSlots, ySlots) =
+ when (tasks) {
+ 2 -> Pair(2, 1)
+ 3,
+ 4 -> Pair(2, 2)
+ 5,
+ 6 -> Pair(3, 2)
+ else -> {
+ val sides = ceil(sqrt(tasks.toDouble())).toInt()
+ Pair(sides, sides)
+ }
+ }
+
+ // The width and height of one of the boxes.
+ val boxWidth = desktopSize.width / xSlots
+ val boxHeight = desktopSize.height / ySlots
+
+ return (0 until tasks).map {
+ val x = it % xSlots
+ val y = it / xSlots
+ Rect(x * boxWidth, y * boxHeight, (x + 1) * boxWidth, (y + 1) * boxHeight)
+ }
+ }
+
+ /** Centers and fits `rect` into `bounds`, while preserving the former's aspect ratio. */
+ private fun fitRect(rect: Rect, bounds: Rect): Rect {
+ val boundsAspect = bounds.width().toFloat() / bounds.height()
+ val rectAspect = rect.width().toFloat() / rect.height()
+
+ if (rectAspect > boundsAspect) {
+ // The width is the limiting dimension.
+ val scale = bounds.width().toFloat() / rect.width()
+ val width = bounds.width()
+ val height = (rect.height() * scale).toInt()
+ val top = (bounds.top + bounds.height() / 2.0f - height / 2.0f).toInt()
+ return Rect(bounds.left, top, bounds.left + width, top + height)
+ } else {
+ // The height is the limiting dimension.
+ val scale = bounds.height().toFloat() / rect.height()
+ val width = (rect.width() * scale).toInt()
+ val height = bounds.height()
+ val left = (bounds.left + bounds.width() / 2.0f - width / 2.0f).toInt()
+ return Rect(left, bounds.top, left + width, bounds.top + height)
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/DesktopTaskViewModel.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/DesktopTaskViewModel.kt
new file mode 100644
index 0000000..0a60ee9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/DesktopTaskViewModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 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.recents.ui.viewmodel
+
+import android.util.Size
+import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
+import com.android.quickstep.recents.domain.usecase.OrganizeDesktopTasksUseCase
+
+/** ViewModel used for [com.android.quickstep.views.DesktopTaskView]. */
+class DesktopTaskViewModel(private val organizeDesktopTasksUseCase: OrganizeDesktopTasksUseCase) {
+ var organizedDesktopTaskPositions = emptyList<DesktopTaskBoundsData>()
+ private set
+
+ fun organizeDesktopTasks(desktopSize: Size, defaultPositions: List<DesktopTaskBoundsData>) {
+ organizedDesktopTaskPositions =
+ organizeDesktopTasksUseCase.run(desktopSize, defaultPositions)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
index 4936e30..961446f 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
@@ -68,8 +68,6 @@
)
}
- val tintAmount: Flow<Float> = recentsViewData.tintAmount
-
val state: Flow<TaskTileUiState> =
combine(taskData, isLiveTile) { tasks, isLiveTile -> mapToTaskTile(tasks, isLiveTile) }
.distinctUntilChanged()
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
index 6ccf372..a1f8454 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
@@ -20,8 +20,6 @@
// This is far from complete but serves the purpose of enabling refactoring in other areas
class RecentsViewData {
- val fullscreenProgress = MutableStateFlow(1f)
-
// Whether the current RecentsView state supports task overlays.
// TODO(b/331753115): Derive from RecentsView state flow once migrated to MVVM.
val overlayEnabled = MutableStateFlow(false)
@@ -29,9 +27,6 @@
// The settled set of visible taskIds that is updated after RecentsView scroll settles.
val settledFullyVisibleTaskIds = MutableStateFlow(emptySet<Int>())
- // Color tint on foreground scrim
- val tintAmount = MutableStateFlow(0f)
-
val thumbnailSplashProgress = MutableStateFlow(0f)
// A list of taskIds that are associated with a RecentsAnimationController. */
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index cfebb81..73332fc 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -34,10 +34,6 @@
recentsTasksRepository.setVisibleTasks(visibleTaskIdList.toSet())
}
- fun updateFullscreenProgress(fullscreenProgress: Float) {
- recentsViewData.fullscreenProgress.value = fullscreenProgress
- }
-
fun updateTasksFullyVisible(taskIds: Set<Int>) {
recentsViewData.settledFullyVisibleTaskIds.value = taskIds
}
@@ -46,10 +42,6 @@
recentsViewData.overlayEnabled.value = isOverlayEnabled
}
- fun setTintAmount(tintAmount: Float) {
- recentsViewData.tintAmount.value = tintAmount
- }
-
fun updateThumbnailSplashProgress(taskThumbnailSplashAlpha: Float) {
recentsViewData.thumbnailSplashProgress.value = taskThumbnailSplashAlpha
}
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index 31aca03..fda0c29 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -78,8 +78,7 @@
@NonNull SplitAnimationController animationController) {
StateAnimationConfig config = new StateAnimationConfig();
BaseState startState = stateManager.getState();
- long duration = startState.getTransitionDuration(container.asContext(),
- false /*isToState*/);
+ long duration = startState.getTransitionDuration(container, false /*isToState*/);
if (duration == 0) {
// Case where we're in contextual on workspace (NORMAL), which by default has 0
// transition duration
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index a1e55fb..09e9c8b 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -120,6 +120,9 @@
private int mTaskRectTranslationY;
private int mDesktopTaskIndex = 0;
+ @Nullable
+ private Matrix mTaskRectTransform = null;
+
public TaskViewSimulator(Context context, BaseContainerInterface sizeStrategy,
boolean isDesktop, int desktopTaskIndex) {
mContext = context;
@@ -364,6 +367,15 @@
}
/**
+ * Sets a matrix used to transform the position of tasks. If set, this matrix is applied to
+ * the task rect after the task has been scaled and positioned inside the fulltask, but
+ * before scaling and translation of the whole recents view is performed.
+ */
+ public void setTaskRectTransform(@Nullable Matrix taskRectTransform) {
+ mTaskRectTransform = taskRectTransform;
+ }
+
+ /**
* Applies the rotation on the matrix to so that it maps from launcher coordinate space to
* window coordinate space.
*/
@@ -424,8 +436,11 @@
mMatrix.set(mPositionHelper.getMatrix());
- // Apply TaskView matrix: taskRect, translate
+ // Apply TaskView matrix: taskRect, optional transform, translate
mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
+ if (mTaskRectTransform != null) {
+ mMatrix.postConcat(mTaskRectTransform);
+ }
mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
taskPrimaryTranslation.value);
mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 471313a..bb6829a 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -15,33 +15,44 @@
*/
package com.android.quickstep.views
+import android.animation.Animator
import android.annotation.SuppressLint
import android.content.Context
-import android.graphics.Point
+import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.Rect
+import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
+import android.util.Size
import android.view.Gravity
import android.view.View
import android.view.ViewStub
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updateLayoutParams
+import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
+import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.testing.TestLogging
import com.android.launcher3.testing.shared.TestProtocol
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.launcher3.util.ViewPool
+import com.android.launcher3.util.rects.lerpRect
import com.android.launcher3.util.rects.set
import com.android.quickstep.BaseContainerInterface
import com.android.quickstep.DesktopFullscreenDrawParams
import com.android.quickstep.FullscreenDrawParams
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.ViewUtils
+import com.android.quickstep.recents.di.RecentsDependencies
+import com.android.quickstep.recents.di.get
+import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
+import com.android.quickstep.recents.ui.viewmodel.DesktopTaskViewModel
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.util.RecentsOrientedState
import com.android.systemui.shared.recents.model.Task
@@ -79,11 +90,40 @@
} else null
private val tempPointF = PointF()
- private val tempRect = Rect()
+ private val lastComputedTaskSize = Rect()
private lateinit var iconView: TaskViewIcon
private lateinit var contentView: DesktopTaskContentView
private lateinit var backgroundView: View
+ private var viewModel: DesktopTaskViewModel? = null
+
+ /**
+ * Holds the default (user placed) positions of task windows. This can be moved into the
+ * viewModel once RefactorTaskThumbnail has been launched.
+ */
+ private var defaultTaskPositions: List<DesktopTaskBoundsData> = emptyList()
+
+ /**
+ * When enableDesktopExplodedView is enabled, this controls the gradual transition from the
+ * default positions to the organized non-overlapping positions.
+ */
+ var explodeProgress = 0.0f
+ set(value) {
+ field = value
+ positionTaskWindows()
+ }
+
+ var remoteTargetHandles: Array<RemoteTargetHandle>? = null
+ set(value) {
+ field = value
+ positionTaskWindows()
+ }
+
+ private fun getRemoteTargetHandle(taskId: Int): RemoteTargetHandle? =
+ remoteTargetHandles?.firstOrNull {
+ it.transformParams.targetSet.firstAppTargetTaskId == taskId
+ }
+
override fun onFinishInflate() {
super.onFinishInflate()
iconView =
@@ -121,6 +161,113 @@
?.inflate()
}
+ fun startWindowExplodeAnimation(): Animator =
+ AnimatedFloat { progress -> explodeProgress = progress }.animateToValue(0.0f, 1.0f)
+
+ private fun positionTaskWindows() {
+ if (taskContainers.isEmpty()) {
+ return
+ }
+
+ val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+
+ val containerWidth = layoutParams.width
+ val containerHeight = layoutParams.height - thumbnailTopMarginPx
+
+ BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
+
+ val windowWidth = tempPointF.x.toInt()
+ val windowHeight = tempPointF.y.toInt()
+ val scaleWidth = containerWidth / windowWidth.toFloat()
+ val scaleHeight = containerHeight / windowHeight.toFloat()
+
+ taskContainers.forEach {
+ val taskId = it.task.key.id
+ val defaultPosition = defaultTaskPositions.firstOrNull { it.taskId == taskId } ?: return
+ val position =
+ if (enableDesktopExplodedView()) {
+ viewModel!!
+ .organizedDesktopTaskPositions
+ .firstOrNull { it.taskId == taskId }
+ ?.let { organizedPosition ->
+ TEMP_RECT.apply {
+ lerpRect(
+ defaultPosition.bounds,
+ organizedPosition.bounds,
+ explodeProgress,
+ )
+ }
+ } ?: defaultPosition.bounds
+ } else {
+ defaultPosition.bounds
+ }
+
+ if (enableDesktopExplodedView()) {
+ getRemoteTargetHandle(taskId)?.let { remoteTargetHandle ->
+ val fromRect =
+ TEMP_RECTF1.apply {
+ set(defaultPosition.bounds)
+ scale(scaleWidth)
+ offset(
+ lastComputedTaskSize.left.toFloat(),
+ lastComputedTaskSize.top.toFloat(),
+ )
+ }
+ val toRect =
+ TEMP_RECTF2.apply {
+ set(position)
+ scale(scaleWidth)
+ offset(
+ lastComputedTaskSize.left.toFloat(),
+ lastComputedTaskSize.top.toFloat(),
+ )
+ }
+ val transform = Matrix()
+ transform.setRectToRect(fromRect, toRect, Matrix.ScaleToFit.FILL)
+ remoteTargetHandle.taskViewSimulator.setTaskRectTransform(transform)
+ remoteTargetHandle.taskViewSimulator.apply(remoteTargetHandle.transformParams)
+ }
+ }
+
+ val taskLeft = position.left * scaleWidth
+ val taskTop = position.top * scaleHeight
+ val taskWidth = position.width() * scaleWidth
+ val taskHeight = position.height() * scaleHeight
+ // TODO(b/394660950): Revisit the choice to update the layout when explodeProgress == 1.
+ // To run the explode animation in reverse, it may be simpler to use translation/scale
+ // for all cases where the progress is non-zero.
+ if (explodeProgress == 0.0f || explodeProgress == 1.0f) {
+ // Reset scaling and translation that may have been applied during animation.
+ it.snapshotView.apply {
+ scaleX = 1.0f
+ scaleY = 1.0f
+ translationX = 0.0f
+ translationY = 0.0f
+ }
+
+ // Position the task to the same position as it would be on the desktop
+ it.snapshotView.updateLayoutParams<LayoutParams> {
+ gravity = Gravity.LEFT or Gravity.TOP
+ width = taskWidth.toInt()
+ height = taskHeight.toInt()
+ leftMargin = taskLeft.toInt()
+ topMargin = taskTop.toInt()
+ }
+ } else {
+ // During the animation, apply translation and scale such that the view is
+ // transformed to where we want, without triggering layout.
+ it.snapshotView.apply {
+ pivotX = 0.0f
+ pivotY = 0.0f
+ translationX = taskLeft - left
+ translationY = taskTop - top
+ scaleX = taskWidth / width.toFloat()
+ scaleY = taskHeight / height.toFloat()
+ }
+ }
+ }
+ }
+
/** Updates this desktop task to the gives task list defined in `tasks` */
fun bind(
tasks: List<Task>,
@@ -133,6 +280,7 @@
tasks.forEach { sb.append(" key=${it.key}\n") }
Log.d(TAG, sb.toString())
}
+
cancelPendingLoadTasks()
val backgroundViewIndex = contentView.indexOfChild(backgroundView)
taskContainers =
@@ -160,8 +308,19 @@
onBind(orientedState)
}
+ override fun onBind(orientedState: RecentsOrientedState) {
+ super.onBind(orientedState)
+
+ if (enableRefactorTaskThumbnail()) {
+ viewModel =
+ DesktopTaskViewModel(organizeDesktopTasksUseCase = RecentsDependencies.get())
+ }
+ }
+
override fun onRecycle() {
super.onRecycle()
+ explodeProgress = 0.0f
+ viewModel = null
visibility = VISIBLE
taskContainers.forEach {
contentView.removeView(it.snapshotView)
@@ -176,61 +335,21 @@
@SuppressLint("RtlHardcoded")
override fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
- if (taskContainers.isEmpty()) {
- return
- }
-
- val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
-
- val containerWidth = layoutParams.width
- val containerHeight = layoutParams.height - thumbnailTopMarginPx
+ this.lastComputedTaskSize.set(lastComputedTaskSize)
BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
+ val desktopSize = Size(tempPointF.x.toInt(), tempPointF.y.toInt())
+ DEFAULT_BOUNDS.set(0, 0, desktopSize.width / 4, desktopSize.height / 4)
- val windowWidth = tempPointF.x.toInt()
- val windowHeight = tempPointF.y.toInt()
- val scaleWidth = containerWidth / windowWidth.toFloat()
- val scaleHeight = containerHeight / windowHeight.toFloat()
-
- if (DEBUG) {
- Log.d(
- TAG,
- "onMeasure: container=[$containerWidth,$containerHeight]" +
- "window=[$windowWidth,$windowHeight] scale=[$scaleWidth,$scaleHeight]",
- )
- }
-
- // Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
- taskContainers.forEach {
- // Default to quarter of the desktop if we did not get app bounds.
- val taskSize =
- it.task.appBounds
- ?: tempRect.apply {
- left = 0
- top = 0
- right = windowWidth / 4
- bottom = windowHeight / 4
- }
- val positionInParent = it.task.positionInParent ?: ORIGIN
-
- // Position the task to the same position as it would be on the desktop
- it.snapshotView.updateLayoutParams<LayoutParams> {
- gravity = Gravity.LEFT or Gravity.TOP
- width = (taskSize.width() * scaleWidth).toInt()
- height = (taskSize.height() * scaleHeight).toInt()
- leftMargin = (positionInParent.x * scaleWidth).toInt()
- topMargin = (positionInParent.y * scaleHeight).toInt()
+ defaultTaskPositions =
+ taskContainers.map {
+ DesktopTaskBoundsData(it.task.key.id, it.task.appBounds ?: DEFAULT_BOUNDS)
}
- if (DEBUG) {
- with(it.snapshotView.layoutParams as LayoutParams) {
- Log.d(
- TAG,
- "onMeasure: task=${it.task.key} size=[$width,$height]" +
- " margin=[$leftMargin,$topMargin]",
- )
- }
- }
+
+ if (enableDesktopExplodedView()) {
+ viewModel?.organizeDesktopTasks(desktopSize, defaultTaskPositions)
}
+ positionTaskWindows()
}
override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
@@ -319,6 +438,10 @@
// As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
private const val VIEW_POOL_INITIAL_SIZE = 0
- private val ORIGIN = Point(0, 0)
+ private val DEFAULT_BOUNDS = Rect()
+ // Temporaries used for various purposes to avoid allocations.
+ private val TEMP_RECT = Rect()
+ private val TEMP_RECTF1 = RectF()
+ private val TEMP_RECTF2 = RectF()
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9c8b249..9d3b23a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -36,6 +36,7 @@
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
+import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableDesktopTaskAlphaAnimation;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
@@ -136,6 +137,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
+import androidx.dynamicanimation.animation.SpringAnimation;
import com.android.internal.jank.Cuj;
import com.android.launcher3.AbstractFloatingView;
@@ -165,6 +167,7 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.IntArray;
@@ -239,6 +242,7 @@
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
+import kotlin.jvm.functions.Function0;
import kotlinx.coroutines.CoroutineScope;
@@ -2186,9 +2190,6 @@
public void setFullscreenProgress(float fullscreenProgress) {
mFullscreenProgress = fullscreenProgress;
- if (enableRefactorTaskThumbnail()) {
- mRecentsViewModel.updateFullscreenProgress(mFullscreenProgress);
- }
for (TaskView taskView : getTaskViews()) {
taskView.setFullscreenProgress(mFullscreenProgress);
}
@@ -2898,7 +2899,7 @@
*/
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
- TaskViewSimulator[] taskViewSimulators) {
+ RemoteTargetHandle[] remoteTargetHandles) {
Log.d(TAG, "onPrepareGestureEndAnimation - endTarget: " + endTarget);
mCurrentGestureEndTarget = endTarget;
boolean isOverviewEndTarget = endTarget == GestureState.GestureEndTarget.RECENTS;
@@ -2906,6 +2907,19 @@
updateGridProperties();
}
+ if (enableDesktopExplodedView()) {
+ for (TaskView taskView : getTaskViews()) {
+ if (taskView instanceof DesktopTaskView desktopTaskView) {
+ if (animatorSet == null) {
+ desktopTaskView.setExplodeProgress(1.0f);
+ } else {
+ animatorSet.play(desktopTaskView.startWindowExplodeAnimation());
+ }
+ desktopTaskView.setRemoteTargetHandles(remoteTargetHandles);
+ }
+ }
+ }
+
BaseState<?> endState = mSizeStrategy.stateFromGestureEndTarget(endTarget);
if (endState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
TaskView runningTaskView = getRunningTaskView();
@@ -2918,7 +2932,8 @@
- runningTaskView.getNonGridTranslationX();
runningTaskSecondaryGridTranslation = runningTaskView.getGridTranslationY();
}
- for (TaskViewSimulator tvs : taskViewSimulators) {
+ for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) {
+ TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
if (animatorSet == null) {
setGridProgress(1);
tvs.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation;
@@ -2966,6 +2981,12 @@
startIconFadeInOnGestureComplete();
animateActionsViewIn();
+ for (TaskView taskView : getTaskViews()) {
+ if (taskView instanceof DesktopTaskView desktopTaskView) {
+ desktopTaskView.setRemoteTargetHandles(mRemoteTargetHandles);
+ }
+ }
+
mCurrentGestureEndTarget = null;
}
@@ -4089,9 +4110,8 @@
} else {
removeTaskInternal(dismissedTaskView);
}
- // TODO(b/391918297): Logging when the TaskView does not have tasks as well.
mContainer.getStatsLogManager().logger()
- .withItemInfo(dismissedTaskView.getFirstItemInfo())
+ .withItemInfo(dismissedTaskView.getItemInfo())
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
}
@@ -5703,6 +5723,13 @@
mTempRect, mContainer.getDeviceProfile(), mTempPointF);
}
+ /**
+ * Clears the existing PendingAnimation.
+ */
+ public void clearPendingAnimation() {
+ mPendingAnimation = null;
+ }
+
public PendingAnimation createTaskLaunchAnimation(
TaskView taskView, long duration, Interpolator interpolator) {
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
@@ -5776,12 +5803,8 @@
} else {
taskView.launchWithoutAnimation(this::onTaskLaunchAnimationEnd);
}
- // TODO(b/391918297): Logging when there is no associated task.
- ItemInfo firstItemInfo = taskView.getFirstItemInfo();
- if (firstItemInfo != null) {
- mContainer.getStatsLogManager().logger().withItemInfo(firstItemInfo)
- .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
- }
+ mContainer.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
+ .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
} else {
onTaskLaunchAnimationEnd(false);
}
@@ -5864,6 +5887,10 @@
mEnableDrawingLiveTile = enableDrawingLiveTile;
}
+ public boolean getEnableDrawingLiveTile() {
+ return mEnableDrawingLiveTile;
+ }
+
public void redrawLiveTile() {
runActionOnRemoteHandles(remoteTargetHandle -> {
TransformParams params = remoteTargetHandle.getTransformParams();
@@ -6556,10 +6583,6 @@
private void setColorTint(float tintAmount) {
mColorTint = tintAmount;
- if (enableRefactorTaskThumbnail()) {
- mRecentsViewModel.setTintAmount(tintAmount);
- }
-
for (TaskView taskView : getTaskViews()) {
taskView.setColorTint(mColorTint, mTintingColor);
}
@@ -6897,6 +6920,19 @@
return Typeface.Builder.NORMAL_WEIGHT;
}
+ /**
+ * Creates the spring animations which run as a task settles back into its place in overview.
+ *
+ * <p>When a task dismiss is cancelled, the task will return to its original position via a
+ * spring animation.
+ */
+ public SpringAnimation createTaskDismissSettlingSpringAnimation(TaskView draggedTaskView,
+ float velocity, boolean isDismissing, SingleAxisSwipeDetector detector,
+ int dismissLength, Function0<Unit> onEndRunnable) {
+ return mUtils.createTaskDismissSettlingSpringAnimation(draggedTaskView, velocity,
+ isDismissing, detector, dismissLength, onEndRunnable);
+ }
+
public interface TaskLaunchListener {
void onTaskLaunched();
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index b1a4808..e61d402 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -63,19 +63,6 @@
<T extends View> T getOverviewPanel();
/**
- * Returns the RootView
- */
- View getRootView();
-
- /**
- * Dispatches a generic motion event to the view hierarchy.
- * Returns the current RecentsViewContainer as context
- */
- default Context asContext() {
- return (Context) this;
- }
-
- /**
* @see Window.Callback#dispatchGenericMotionEvent(MotionEvent)
*/
boolean dispatchGenericMotionEvent(MotionEvent ev);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 94e8c03..f610335 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -19,14 +19,21 @@
import android.graphics.Rect
import android.view.View
import androidx.core.view.children
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
+import com.android.launcher3.R
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.DynamicResource
import com.android.launcher3.util.IntArray
import com.android.quickstep.util.GroupTask
import com.android.quickstep.util.isExternalDisplay
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
import com.android.systemui.shared.recents.model.ThumbnailData
import java.util.function.BiConsumer
+import kotlin.math.abs
/**
* Helper class for [RecentsView]. This util class contains refactored and extracted functions from
@@ -294,6 +301,58 @@
}
}
+ /**
+ * Creates the spring animations which run when a dragged task view in overview is released.
+ *
+ * <p>When a task dismiss is cancelled, the task will return to its original position via a
+ * spring animation.
+ */
+ fun createTaskDismissSettlingSpringAnimation(
+ draggedTaskView: TaskView?,
+ velocity: Float,
+ isDismissing: Boolean,
+ detector: SingleAxisSwipeDetector,
+ dismissLength: Int,
+ onEndRunnable: () -> Unit,
+ ): SpringAnimation? {
+ draggedTaskView ?: return null
+ val taskDismissFloatProperty =
+ FloatPropertyCompat.createFloatPropertyCompat(
+ draggedTaskView.secondaryDismissTranslationProperty
+ )
+ val rp = DynamicResource.provider(recentsView.mContainer)
+ return SpringAnimation(draggedTaskView, taskDismissFloatProperty)
+ .setSpring(
+ SpringForce()
+ .setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
+ .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness))
+ )
+ .setStartVelocity(if (detector.isFling(velocity)) velocity else 0f)
+ .addUpdateListener { animation, value, _ ->
+ if (isDismissing && abs(value) >= abs(dismissLength)) {
+ // TODO(b/393553524): Remove 0 alpha, instead animate task fully off screen.
+ draggedTaskView.alpha = 0f
+ animation.cancel()
+ } else if (draggedTaskView.isRunningTask && recentsView.enableDrawingLiveTile) {
+ recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
+ remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation.value =
+ taskDismissFloatProperty.getValue(draggedTaskView)
+ }
+ recentsView.redrawLiveTile()
+ }
+ }
+ .addEndListener { _, _, _, _ ->
+ if (isDismissing) {
+ recentsView.dismissTask(
+ draggedTaskView,
+ /* animateTaskView = */ false,
+ /* removeTask = */ true,
+ )
+ }
+ onEndRunnable()
+ }
+ }
+
companion object {
val TEMP_RECT = Rect()
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 0350c78..b6f6bed 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -106,7 +106,7 @@
/** Builds proto for logging */
val itemInfo: TaskViewItemInfo
- get() = TaskViewItemInfo(this)
+ get() = TaskViewItemInfo(taskView, this)
fun bind() {
digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index ce0efb6..4b1b8dc 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -33,6 +33,7 @@
import android.view.Display
import android.view.MotionEvent
import android.view.View
+import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
@@ -54,6 +55,7 @@
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.testing.TestLogging
import com.android.launcher3.testing.shared.TestProtocol
import com.android.launcher3.util.CancellableTask
@@ -98,8 +100,6 @@
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
/** A task in the Recents view. */
@@ -169,6 +169,15 @@
val firstItemInfo: ItemInfo?
get() = firstTaskContainer?.itemInfo
+ /**
+ * A [TaskViewItemInfo] of this TaskView. The [firstTaskContainer] will be used to get some
+ * specific information like user, title etc of the Task. However, these task specific
+ * information will be skipped if the TaskView has no [taskContainers]. Note, please use
+ * [TaskContainer.itemInfo] for [TaskViewItemInfo] on a specific [TaskContainer].
+ */
+ val itemInfo: TaskViewItemInfo
+ get() = TaskViewItemInfo(this, firstTaskContainer)
+
protected val container: RecentsViewContainer =
RecentsViewContainer.containerFromContext(context)
protected val lastTouchDownPosition = PointF()
@@ -213,7 +222,7 @@
get() =
pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
- protected val secondaryDismissTranslationProperty: FloatProperty<TaskView>
+ val secondaryDismissTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
@@ -743,17 +752,10 @@
// onRecycle. So it should be initialized at this point. TaskView Lifecycle:
// `bind` -> `onBind` -> onAttachedToWindow() -> onDetachFromWindow -> onRecycle
coroutineJobs +=
- viewModel!!.tintAmount.onEach(::updateTintAmount).launchIn(coroutineScope)
-
- coroutineJobs +=
coroutineScope.launch { viewModel!!.state.collectLatest(::updateTaskViewState) }
}
}
- private fun updateTintAmount(amount: Float) {
- taskContainers.forEach { it.updateTintAmount(amount) }
- }
-
private fun updateTaskViewState(state: TaskTileUiState) {
sysUiStatusNavFlags = state.sysUiStatusNavFlags
@@ -1091,13 +1093,10 @@
}
}
Log.d("b/310064698", "${taskIds.contentToString()} - onClick - callbackList: $callbackList")
- // TODO(b/391918297): Logging when there is no associated task.
- firstItemInfo?.let {
- container.statsLogManager
- .logger()
- .withItemInfo(it)
- .log(LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP)
- }
+ container.statsLogManager
+ .logger()
+ .withItemInfo(itemInfo)
+ .log(LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP)
}
/** Launch of the current task (both live and inactive tasks) with an animation. */
@@ -1531,7 +1530,9 @@
/** Set a color tint on the snapshot and supporting views. */
open fun setColorTint(amount: Float, tintColor: Int) {
taskContainers.forEach {
- if (!enableRefactorTaskThumbnail()) {
+ if (enableRefactorTaskThumbnail()) {
+ it.updateTintAmount(amount)
+ } else {
it.thumbnailViewDeprecated.dimAlpha = amount
}
it.iconView.setIconColorTint(tintColor, amount)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
index be71640..de0da64 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
@@ -18,6 +18,7 @@
import android.content.ComponentName
import android.content.Intent
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
@@ -33,7 +34,6 @@
import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
-import com.android.quickstep.TestComponent
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.RecentsView
@@ -84,7 +84,7 @@
whenever(taskView.type).thenReturn(TaskViewType.SINGLE)
whenever(taskView.taskContainers).thenReturn(taskContainers)
- val taskViewItemInfo = TaskViewItemInfo(taskContainers[0])
+ val taskViewItemInfo = TaskViewItemInfo(taskContainers[0].taskView, taskContainers[0])
assertThat(taskViewItemInfo.taskViewAtom)
.isEqualTo(
@@ -105,7 +105,7 @@
whenever(taskView.type).thenReturn(TaskViewType.GROUPED)
whenever(taskView.taskContainers).thenReturn(taskContainers)
- val taskViewItemInfo = TaskViewItemInfo(taskContainers[0])
+ val taskViewItemInfo = TaskViewItemInfo(taskContainers[0].taskView, taskContainers[0])
assertThat(taskViewItemInfo.taskViewAtom)
.isEqualTo(
@@ -130,7 +130,7 @@
whenever(taskView.type).thenReturn(TaskViewType.DESKTOP)
whenever(taskView.taskContainers).thenReturn(taskContainers)
- val taskViewItemInfo = TaskViewItemInfo(taskContainers[0])
+ val taskViewItemInfo = TaskViewItemInfo(taskContainers[0].taskView, taskContainers[0])
assertThat(taskViewItemInfo.taskViewAtom)
.isEqualTo(
@@ -151,7 +151,7 @@
whenever(taskView.taskContainers).thenReturn(taskContainers)
whenever(userInfo.isPrivate).thenReturn(true)
- val taskViewItemInfo = TaskViewItemInfo(taskContainers[0])
+ val taskViewItemInfo = TaskViewItemInfo(taskContainers[0].taskView, taskContainers[0])
assertThat(taskViewItemInfo.taskViewAtom)
.isEqualTo(
@@ -166,6 +166,25 @@
.isEqualTo(FLAG_NOT_PINNABLE)
}
+ @Test
+ fun emptyDesktopTask() {
+ whenever(taskView.type).thenReturn(TaskViewType.DESKTOP)
+
+ val taskViewItemInfo = TaskViewItemInfo(taskView = taskView, taskContainer = null)
+
+ assertThat(taskViewItemInfo.taskViewAtom)
+ .isEqualTo(
+ createTaskViewAtom(
+ type = 2,
+ index = TASK_VIEW_INDEX,
+ componentName = "",
+ cardinality = 0,
+ )
+ )
+ assertThat(taskViewItemInfo.user).isEqualTo(Process.myUserHandle())
+ assertThat(taskViewItemInfo.intent).isNotNull()
+ }
+
private fun createTask(id: Int) =
Task(TaskKey(id, 0, Intent(), ComponentName(PACKAGE, CLASS), 0, 2000))
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java
index 99a1c59..722e1da 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsModelTest.java
@@ -43,6 +43,7 @@
import com.android.launcher3.R;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SplitTask;
@@ -76,6 +77,12 @@
@Mock
private HighResLoadingState mHighResLoadingState;
+ @Mock
+ private LockedUserState mLockedUserState;
+
+ @Mock
+ private ThemeManager mThemeManager;
+
private RecentsModel mRecentsModel;
private RecentTasksList.TaskLoadResult mTaskResult;
@@ -102,7 +109,7 @@
mRecentsModel = new RecentsModel(mContext, mTasksList, mock(TaskIconCache.class),
mThumbnailCache, mock(IconProvider.class), mock(TaskStackChangeListeners.class),
- mock(ThemeManager.class));
+ mLockedUserState, () -> mThemeManager);
mResource = mock(Resources.class);
when(mResource.getInteger((R.integer.recentsThumbnailCacheSize))).thenReturn(3);
@@ -167,6 +174,17 @@
.updateThumbnailInCache(any(), anyBoolean());
}
+ @Test
+ public void themeCallbackAttachedOnUnlock() {
+ verify(mThemeManager, never()).addChangeListener(any());
+
+ ArgumentCaptor<Runnable> callbackCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mLockedUserState).runOnUserUnlocked(callbackCaptor.capture());
+
+ callbackCaptor.getAllValues().forEach(Runnable::run);
+ verify(mThemeManager, times(1)).addChangeListener(any());
+ }
+
private RecentTasksList.TaskLoadResult getTaskResult() {
RecentTasksList.TaskLoadResult allTasks = new RecentTasksList.TaskLoadResult(0, false, 1);
ActivityManager.RecentTaskInfo taskInfo1 = new ActivityManager.RecentTaskInfo();
diff --git a/res/layout/work_mode_utility_view.xml b/res/layout/work_mode_utility_view.xml
index fc112ce..b68ff3e 100644
--- a/res/layout/work_mode_utility_view.xml
+++ b/res/layout/work_mode_utility_view.xml
@@ -14,6 +14,7 @@
~ limitations under the License.
-->
<com.android.launcher3.allapps.WorkUtilityView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/work_utility_view"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 0b8e52a..2b7ec21 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -24,7 +24,7 @@
<string name="activity_not_found" msgid="8071924732094499514">"لم يتم تثبيت التطبيق."</string>
<string name="activity_not_available" msgid="7456344436509528827">"التطبيق ليس متاحًا"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"تم إيقاف التطبيق الذي تم تنزيله في الوضع الآمن"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"الأدوات غير مفعّلة في الوضع الآمن"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"التطبيقات المصغَّرة غير مفعّلة في الوضع الآمن"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"الاختصار غير متاح"</string>
<string name="home_screen" msgid="5629429142036709174">"الشاشة الرئيسية"</string>
<string name="set_default_home_app" msgid="5808906607627586381">"يمكن ضبط \"<xliff:g id="LAUNCHER_NAME">%1$s</xliff:g>\" كتطبيق الشاشة الرئيسية التلقائي من خلال \"الإعدادات\""</string>
@@ -62,7 +62,7 @@
<string name="widget_button_text" msgid="2880537293434387943">"التطبيقات المصغّرة"</string>
<string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"بحث"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"محو النص من مربّع البحث"</string>
- <string name="no_widgets_available" msgid="4337693382501046170">"الأدوات والاختصارات غير متاحة."</string>
+ <string name="no_widgets_available" msgid="4337693382501046170">"التطبيقات المصغَّرة والاختصارات غير متاحة."</string>
<string name="no_search_results" msgid="3787956167293097509">"لم يتم العثور على تطبيقات مصغّرة أو اختصارات."</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"التطبيقات الشخصية"</string>
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"تطبيقات العمل"</string>
@@ -159,8 +159,8 @@
<string name="dialog_update_message" msgid="4176784553982226114">"لم يتمّ تحديث التطبيق الخاص بهذا الرمز. يمكنك تحديث التطبيق يدويًا لإعادة تفعيل هذا الاختصار أو إزالة الرمز."</string>
<string name="dialog_update" msgid="2178028071796141234">"تحديث"</string>
<string name="dialog_remove" msgid="6510806469849709407">"إزالة"</string>
- <string name="widgets_list" msgid="796804551140113767">"قائمة الأدوات"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"تم إغلاق قائمة الأدوات."</string>
+ <string name="widgets_list" msgid="796804551140113767">"قائمة التطبيقات المصغَّرة"</string>
+ <string name="widgets_list_closed" msgid="6141506579418771922">"تم إغلاق قائمة التطبيقات المصغَّرة."</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"إضافة تطبيق للشاشة الرئيسية"</string>
<string name="action_move_here" msgid="2170188780612570250">"نقل العنصر إلى هنا"</string>
<string name="item_removed" msgid="851119963877842327">"تمّت إزالة العنصر."</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 78b566a..e3bde3a 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -83,7 +83,7 @@
<string name="label_application" msgid="8531721983832654978">"एप"</string>
<string name="all_apps_label" msgid="5015784846527570951">"सबै एप"</string>
<string name="all_apps_list_label" msgid="5106226764073070906">"एपहरूको सूची"</string>
- <string name="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"नोटिफिकेसनहरू"</string>
<string name="long_press_shortcut_to_add" msgid="5405328730817637737">"कुनै सर्टकट सार्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"कुनै सर्टकट सार्न वा आफ्नो रोजाइका कारबाही प्रयोग गर्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
<string name="out_of_space" msgid="6455557115204099579">"यो होम स्क्रिनमा ठाउँ छैन"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 7016df5..9e2307a 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -129,7 +129,7 @@
<string name="app_pair_name_format" msgid="8134106404716224054">"Par aplikacij: <xliff:g id="APP1">%1$s</xliff:g> in <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Zaslonsko ozadje in slog"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Urejanje začetnega zaslona"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Domače nastavitve"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Začetni zaslon"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Dovoli sukanje začetnega zaslona"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Ko se telefon zasuka"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 212534b..c48f140 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -191,7 +191,7 @@
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
- <dimen name="widget_cell_horizontal_padding">8dp</dimen>
+ <dimen name="widget_cell_horizontal_padding">4dp</dimen>
<dimen name="widget_cell_title_font_size">14sp</dimen>
<integer name="widget_cell_title_font_weight">500</integer>
<dimen name="widget_cell_title_line_height">20sp</dimen>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 2e75261..6277e41 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
@@ -29,17 +30,27 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
+import android.view.ActionMode;
+import android.view.View;
import android.window.OnBackInvokedDispatcher;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.ScrimView;
@@ -52,7 +63,8 @@
/**
* Launcher BaseActivity
*/
-public abstract class BaseActivity extends Activity implements ActivityContext {
+public abstract class BaseActivity extends Activity implements ActivityContext,
+ DisplayInfoChangeListener {
private static final String TAG = "BaseActivity";
static final boolean DEBUG = false;
@@ -126,6 +138,10 @@
public @interface ActivityFlags {
}
+ // When starting an action mode, setting this tag will cause the action mode to be cancelled
+ // automatically when user interacts with the launcher.
+ public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
/** Returns a human-readable string for the specified {@link ActivityFlags}. */
public static String getActivityStateString(@ActivityFlags int flags) {
StringJoiner result = new StringJoiner("|");
@@ -160,6 +176,8 @@
private final RunnableList[] mEventCallbacks =
{new RunnableList(), new RunnableList(), new RunnableList(), new RunnableList()};
+ private ActionMode mCurrentActionMode;
+
@Override
public ViewCache getViewCache() {
return mViewCache;
@@ -206,6 +224,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerBackDispatcher();
+ DisplayController.INSTANCE.get(this).addChangeListener(this);
}
@Override
@@ -253,6 +272,7 @@
protected void onDestroy() {
super.onDestroy();
mEventCallbacks[EVENT_DESTROYED].executeAllAndClear();
+ DisplayController.INSTANCE.get(this).removeChangeListener(this);
}
@Override
@@ -403,6 +423,61 @@
writer.println(prefix + "mForceInvisible: " + mForceInvisible);
}
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ mCurrentActionMode = mode;
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ super.onActionModeFinished(mode);
+ mCurrentActionMode = null;
+ }
+
+ protected boolean isInAutoCancelActionMode() {
+ return mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag();
+ }
+
+ @Override
+ public boolean finishAutoCancelActionMode() {
+ if (isInAutoCancelActionMode()) {
+ mCurrentActionMode.finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ @NonNull
+ public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+ ActivityOptionsWrapper wrapper = ActivityContext.super.getActivityLaunchOptions(v, item);
+ addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
+ return wrapper;
+ }
+
+ @Override
+ public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+ ActivityOptionsWrapper wrapper =
+ ActivityContext.super.makeDefaultActivityOptions(splashScreenStyle);
+ addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
+ return wrapper;
+ }
+
+ protected WindowBounds getMultiWindowDisplaySize() {
+ return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
+ }
+
+ @Override
+ public void onDisplayInfoChanged(Context context, Info info, int flags) {
+ if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.isVerticalBarLayout()) {
+ reapplyUi();
+ }
+ }
+
+ protected void reapplyUi() {}
+
public static <T extends BaseActivity> T fromContext(Context context) {
if (context instanceof BaseActivity) {
return (T) context;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
deleted file mode 100644
index 3b93cf4..0000000
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.view.ActionMode;
-import android.view.View;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
-import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.OnColorHintListener;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.WallpaperColorHints;
-import com.android.launcher3.util.WindowBounds;
-
-/**
- * Extension of BaseActivity allowing support for drag-n-drop
- */
-@SuppressWarnings("NewApi")
-public abstract class BaseDraggingActivity extends BaseActivity
- implements OnColorHintListener, DisplayInfoChangeListener {
-
- // When starting an action mode, setting this tag will cause the action mode to be cancelled
- // automatically when user interacts with the launcher.
- public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
-
- private ActionMode mCurrentActionMode;
-
- private int mThemeRes = R.style.AppTheme;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- DisplayController.INSTANCE.get(this).addChangeListener(this);
-
- // Update theme
- WallpaperColorHints.get(this).registerOnColorHintsChangedListener(this);
- int themeRes = Themes.getActivityThemeRes(this);
- if (themeRes != mThemeRes) {
- mThemeRes = themeRes;
- setTheme(themeRes);
- }
- }
-
- @MainThread
- @Override
- public void onColorHintsChanged(int colorHints) {
- updateTheme();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateTheme();
- }
-
- private void updateTheme() {
- if (mThemeRes != Themes.getActivityThemeRes(this)) {
- recreateToUpdateTheme();
- }
- }
-
- protected void recreateToUpdateTheme() {
- recreate();
- }
-
- @Override
- public void onActionModeStarted(ActionMode mode) {
- super.onActionModeStarted(mode);
- mCurrentActionMode = mode;
- }
-
- @Override
- public void onActionModeFinished(ActionMode mode) {
- super.onActionModeFinished(mode);
- mCurrentActionMode = null;
- }
-
- protected boolean isInAutoCancelActionMode() {
- return mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag();
- }
-
- @Override
- public boolean finishAutoCancelActionMode() {
- if (isInAutoCancelActionMode()) {
- mCurrentActionMode.finish();
- return true;
- }
- return false;
- }
-
- public abstract View getRootView();
-
- public void returnToHomescreen() {
- // no-op
- }
-
- @Override
- @NonNull
- public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
- ActivityOptionsWrapper wrapper = super.getActivityLaunchOptions(v, item);
- addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
- return wrapper;
- }
-
- @Override
- public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
- ActivityOptionsWrapper wrapper = super.makeDefaultActivityOptions(splashScreenStyle);
- addEventCallback(EVENT_RESUMED, wrapper.onEndCallback::executeAllAndDestroy);
- return wrapper;
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- DisplayController.INSTANCE.get(this).removeChangeListener(this);
- WallpaperColorHints.get(this).unregisterOnColorsChangedListener(this);
- }
-
- protected void onDeviceProfileInitiated() {
- }
-
- @Override
- public void onDisplayInfoChanged(Context context, Info info, int flags) {
- if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.isVerticalBarLayout()) {
- reapplyUi();
- }
- }
-
- @Override
- public View.OnClickListener getItemOnClickListener() {
- return ItemClickHandler.INSTANCE;
- }
-
- protected abstract void reapplyUi();
-
- protected WindowBounds getMultiWindowDisplaySize() {
- return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
- }
-
- @Override
- public boolean isAppBlockedForSafeMode() {
- return LauncherAppState.getInstance(this).isSafeModeEnabled();
- }
-}
diff --git a/src/com/android/launcher3/DropTargetHandler.kt b/src/com/android/launcher3/DropTargetHandler.kt
index 66c948a..0cc7fc7 100644
--- a/src/com/android/launcher3/DropTargetHandler.kt
+++ b/src/com/android/launcher3/DropTargetHandler.kt
@@ -2,7 +2,7 @@
import android.content.ComponentName
import android.view.View
-import com.android.launcher3.BaseDraggingActivity.EVENT_RESUMED
+import com.android.launcher3.BaseActivity.EVENT_RESUMED
import com.android.launcher3.DropTarget.DragObject
import com.android.launcher3.LauncherConstants.ActivityCodes
import com.android.launcher3.SecondaryDropTarget.DeferredOnComplete
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 647d2ad..30ef24b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -102,6 +102,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING;
+import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -144,6 +145,7 @@
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowInsets;
@@ -222,6 +224,7 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.BackPressHandler;
@@ -466,6 +469,7 @@
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
+ .detectActivityLeaks()
.penaltyLog()
.penaltyDeath()
.build());
@@ -509,6 +513,7 @@
}
super.onCreate(savedInstanceState);
+ setWallpaperDependentTheme(this);
LauncherAppState app = LauncherAppState.getInstance(this);
mModel = app.getModel();
@@ -819,7 +824,6 @@
this, getMultiWindowDisplaySize());
}
- onDeviceProfileInitiated();
if (FOLDABLE_SINGLE_PAGE.get() && mDeviceProfile.isTwoPanels) {
mCellPosMapper = new TwoPanelCellPosMapper(mDeviceProfile.inv.numColumns);
} else {
@@ -2840,12 +2844,6 @@
// Overridden
}
- @Override
- public void returnToHomescreen() {
- super.returnToHomescreen();
- getStateManager().goToState(LauncherState.NORMAL);
- }
-
public void closeOpenViews() {
closeOpenViews(true);
}
@@ -3170,5 +3168,10 @@
return findViewById(R.id.popup_container);
}
+ @Override
+ public OnClickListener getItemOnClickListener() {
+ return ItemClickHandler.INSTANCE;
+ }
+
// End of Getters and Setters
}
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 484cef4..1120ec8 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -34,6 +34,7 @@
import com.android.launcher3.states.RotationHelper
import com.android.launcher3.util.DaggerSingletonObject
import com.android.launcher3.util.DisplayController
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
/**
@@ -52,17 +53,18 @@
.getSharedPreferences(BOOT_AWARE_PREFS_KEY, MODE_PRIVATE)
}
- open val Item.sharedPrefs: SharedPreferences
- get() =
+ open protected fun getSharedPrefs(item: Item): SharedPreferences =
+ item.run {
if (encryptionType == EncryptionType.DEVICE_PROTECTED) deviceProtectedSharedPrefs
else encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
+ }
/** Returns the value with type [T] for [item]. */
- open fun <T> get(item: ContextualItem<T>): T =
+ fun <T> get(item: ContextualItem<T>): T =
getInner(item, item.defaultValueFromContext(encryptedContext))
/** Returns the value with type [T] for [item]. */
- open fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
+ fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
/**
* Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
@@ -71,7 +73,7 @@
*/
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
private fun <T> getInner(item: Item, default: T): T {
- val sp = item.sharedPrefs
+ val sp = getSharedPrefs(item)
return when (item.type) {
String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
@@ -127,7 +129,7 @@
private fun prepareToPutValues(
updates: Array<out Pair<Item, Any>>
): List<SharedPreferences.Editor> {
- val updatesPerPrefFile = updates.groupBy { it.first.sharedPrefs }.toMap()
+ val updatesPerPrefFile = updates.groupBy { getSharedPrefs(it.first) }.toMap()
return updatesPerPrefFile.map { (sharedPref, itemList) ->
sharedPref.edit().apply { itemList.forEach { (item, value) -> putValue(item, value) } }
@@ -140,7 +142,7 @@
* types of Item values.
*/
@Suppress("UNCHECKED_CAST")
- private fun SharedPreferences.Editor.putValue(
+ internal fun SharedPreferences.Editor.putValue(
item: Item,
value: Any?,
): SharedPreferences.Editor =
@@ -168,7 +170,7 @@
*/
fun addListener(listener: LauncherPrefChangeListener, vararg items: Item) {
items
- .map { it.sharedPrefs }
+ .map { getSharedPrefs(it) }
.distinct()
.forEach { it.registerOnSharedPreferenceChangeListener(listener) }
}
@@ -180,7 +182,7 @@
fun removeListener(listener: LauncherPrefChangeListener, vararg items: Item) {
// If a listener is not registered to a SharedPreference, unregistering it does nothing
items
- .map { it.sharedPrefs }
+ .map { getSharedPrefs(it) }
.distinct()
.forEach { it.unregisterOnSharedPreferenceChangeListener(listener) }
}
@@ -191,7 +193,7 @@
*/
fun has(vararg items: Item): Boolean {
items
- .groupBy { it.sharedPrefs }
+ .groupBy { getSharedPrefs(it) }
.forEach { (prefs, itemsSublist) ->
if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
}
@@ -215,7 +217,7 @@
* .apply() or .commit()
*/
private fun prepareToRemove(items: Array<out Item>): List<SharedPreferences.Editor> {
- val itemsPerFile = items.groupBy { it.sharedPrefs }.toMap()
+ val itemsPerFile = items.groupBy { getSharedPrefs(it) }.toMap()
return itemsPerFile.map { (prefs, items) ->
prefs.edit().also { editor ->
@@ -412,14 +414,20 @@
*/
class ProxyPrefs(context: Context, private val prefs: SharedPreferences) : LauncherPrefs(context) {
- private val realPrefs = LauncherPrefs(context)
+ private val copiedPrefs = ConcurrentHashMap<SharedPreferences, Boolean>()
- override val Item.sharedPrefs: SharedPreferences
- get() = prefs
-
- override fun <T> get(item: ConstantItem<T>) =
- super.get(backedUpItem(item.sharedPrefKey, realPrefs.get(item)))
-
- override fun <T> get(item: ContextualItem<T>) =
- super.get(backedUpItem(item.sharedPrefKey, realPrefs.get(item)))
+ override fun getSharedPrefs(item: Item): SharedPreferences {
+ val originalPrefs = super.getSharedPrefs(item)
+ // Copy all existing values, when the pref is accessed for the first time
+ copiedPrefs.computeIfAbsent(originalPrefs) { op ->
+ val editor = prefs.edit()
+ op.all.forEach { (key, value) ->
+ if (value != null) {
+ editor.putValue(backedUpItem(key, value), value)
+ }
+ }
+ editor.commit()
+ }
+ return prefs
+ }
}
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index a5b95c7..b8a0abd 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -12,6 +12,7 @@
import com.android.launcher3.graphics.SysUiScrim;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.window.WindowManagerProxy;
+import com.android.launcher3.views.ActivityContext;
import java.util.Collections;
import java.util.List;
@@ -36,7 +37,7 @@
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
- mStatefulContainer = StatefulContainer.fromContext(context);
+ mStatefulContainer = ActivityContext.lookupContext(context);
mSysUiScrim = new SysUiScrim(this);
}
@@ -54,7 +55,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mStatefulContainer.handleConfigurationChanged(
- mStatefulContainer.getContext().getResources().getConfiguration());
+ mStatefulContainer.asContext().getResources().getConfiguration());
return updateInsets(insets);
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 7d5e481..8c6555e 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -118,7 +118,7 @@
LAUNCHER_STATE_HOME,
FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HAS_SYS_UI_SCRIM) {
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
// Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
return 0;
}
@@ -345,7 +345,7 @@
public final <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
float getDepth(DEVICE_PROFILE_CONTEXT context) {
return getDepth(context,
- BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
+ ActivityContext.lookupContext(context).getDeviceProfile().isMultiWindowMode);
}
/**
diff --git a/src/com/android/launcher3/allapps/WorkUtilityView.java b/src/com/android/launcher3/allapps/WorkUtilityView.java
index bccc279..e42a6b9 100644
--- a/src/com/android/launcher3/allapps/WorkUtilityView.java
+++ b/src/com/android/launcher3/allapps/WorkUtilityView.java
@@ -15,6 +15,11 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -46,6 +51,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedPropertySetter;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
@@ -88,6 +94,8 @@
private TextView mPauseText;
private ImageView mWorkIcon;
private ImageButton mSchedulerButton;
+ private final StatsLogManager mStatsLogManager;
+ private LinearLayout mWorkUtilityView;
public WorkUtilityView(@NonNull Context context) {
this(context, null, 0);
@@ -111,6 +119,7 @@
R.dimen.work_fab_icon_start_margin_expanded);
mWorkSchedulerIntentAction = mContext.getResources().getString(
R.string.work_profile_scheduler_intent);
+ mStatsLogManager = mActivityContext.getStatsLogManager();
}
@Override
@@ -121,6 +130,7 @@
mWorkIcon = findViewById(R.id.work_icon);
mWorkFAB = findViewById(R.id.work_mode_toggle);
mSchedulerButton = findViewById(R.id.work_scheduler);
+ mWorkUtilityView = findViewById(R.id.work_utility_view);
setSelected(true);
KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
new KeyboardInsetAnimationCallback(this);
@@ -319,6 +329,23 @@
animatorList.add(animateSchedulerScale(isExpanding));
animatorList.add(animateSchedulerAlpha(isExpanding));
}
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mStatsLogManager.logger().sendToInteractionJankMonitor(
+ isExpanding ? LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN
+ : LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN,
+ mWorkUtilityView);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStatsLogManager.logger().sendToInteractionJankMonitor(
+ isExpanding ? LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END
+ : LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END,
+ mWorkUtilityView);
+ }
+ });
animatorSet.playTogether(animatorList);
animatorSet.start();
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index d2ff2cb..2157610 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -35,6 +35,7 @@
import android.util.Property;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
@@ -79,9 +80,10 @@
private final int mDuration;
private final int mDelay;
- private final TimeInterpolator mFolderInterpolator;
- private final TimeInterpolator mLargeFolderPreviewItemOpenInterpolator;
- private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
+ private final Interpolator mFolderOpenInterpolator;
+ private final Interpolator mFolderCloseInterpolator;
+ private final Interpolator mLargeFolderPreviewItemOpenInterpolator;
+ private final Interpolator mLargeFolderPreviewItemCloseInterpolator;
private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
private final FolderGridOrganizer mPreviewVerifier;
@@ -108,7 +110,9 @@
mDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
mDelay = res.getInteger(R.integer.config_folderDelay);
- mFolderInterpolator = AnimationUtils.loadInterpolator(mContext,
+ mFolderOpenInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.standard_interpolator);
+ mFolderCloseInterpolator = AnimationUtils.loadInterpolator(mContext,
R.interpolator.standard_interpolator);
mLargeFolderPreviewItemOpenInterpolator = AnimationUtils.loadInterpolator(mContext,
R.interpolator.large_folder_preview_item_open_interpolator);
@@ -252,6 +256,7 @@
(int) (left + (startRect.right / initialScale)) + extraRadius,
(int) (startRect.bottom / initialScale) + extraRadius);
Rect contentEnd = new Rect(left, 0, left + lp.width, lp.height);
+ // animated contents of folder with the folder background
play(a, shapeDelegate.createRevealAnimator(
mFolder.getContent(), contentStart, contentEnd, finalRadius, !mIsOpening));
@@ -332,7 +337,11 @@
// We set the interpolator on all current child animators here, because the preview item
// animators may use a different interpolator.
for (Animator animator : a.getChildAnimations()) {
- animator.setInterpolator(mFolderInterpolator);
+ animator.setInterpolator(
+ mIsOpening
+ ? mFolderOpenInterpolator
+ : mFolderCloseInterpolator
+ );
}
int radiusDiff = scaledRadius - mPreviewBackground.getRadius();
@@ -467,7 +476,7 @@
return mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW;
}
- private TimeInterpolator getPreviewItemInterpolator() {
+ private Interpolator getPreviewItemInterpolator() {
if (isLargeFolder()) {
// With larger folders, we want the preview items to reach their final positions faster
// (when opening) and later (when closing) so that they appear aligned with the rest of
@@ -476,7 +485,7 @@
? mLargeFolderPreviewItemOpenInterpolator
: mLargeFolderPreviewItemCloseInterpolator;
}
- return mFolderInterpolator;
+ return mIsOpening ? mFolderOpenInterpolator : mFolderCloseInterpolator;
}
private Animator getAnimator(View view, Property property, float v1, float v2) {
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 9edc386..80743af 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -55,6 +55,7 @@
import java.lang.ref.WeakReference;
import java.util.Collections;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -148,13 +149,23 @@
KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
String currentShapePath =
ThemeManager.INSTANCE.get(context).getIconState().getIconMask();
+ Optional<IconShapeModel> selectedShape = ShapesProvider.INSTANCE.getIconShapes()
+ .values()
+ .stream()
+ .filter(shape -> shape.getPathString().equals(currentShapePath))
+ .findFirst();
+ // Handle default for when current shape doesn't match new shapes.
+ if (selectedShape.isEmpty()) {
+ selectedShape = Optional.ofNullable(ShapesProvider.INSTANCE.getIconShapes()
+ .get("circle"));
+ }
+
for (IconShapeModel shape : ShapesProvider.INSTANCE.getIconShapes().values()) {
cursor.newRow()
.add(KEY_SHAPE_KEY, shape.getKey())
.add(KEY_SHAPE_TITLE, shape.getTitle())
.add(KEY_PATH, shape.getPathString())
- .add(KEY_IS_DEFAULT,
- shape.getPathString().equals(currentShapePath));
+ .add(KEY_IS_DEFAULT, shape.equals(selectedShape.get()));
}
return cursor;
} else {
diff --git a/src/com/android/launcher3/graphics/IconShape.kt b/src/com/android/launcher3/graphics/IconShape.kt
index e62936c..eac3440 100644
--- a/src/com/android/launcher3/graphics/IconShape.kt
+++ b/src/com/android/launcher3/graphics/IconShape.kt
@@ -38,6 +38,7 @@
import androidx.graphics.shapes.Morph
import androidx.graphics.shapes.RoundedPolygon
import androidx.graphics.shapes.SvgPathParser
+import androidx.graphics.shapes.rectangle
import androidx.graphics.shapes.toPath
import androidx.graphics.shapes.transformed
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
@@ -362,8 +363,8 @@
}
/**
- * Creates a rounded rect with the start point at the center of the top edge. This ensures a
- * better animation since our shape paths also start at top-center of the bounding box.
+ * Create RoundedRect using RoundedPolygon API. Ensures smoother animation morphing between
+ * generic polygon by using [RoundedPolygon.Companion.rectangle] directly.
*/
fun createRoundedRect(
left: Float,
@@ -372,27 +373,11 @@
bottom: Float,
cornerR: Float,
) =
- RoundedPolygon(
- vertices =
- floatArrayOf(
- // x1, y1
- (left + right) / 2,
- top,
- // x2, y2
- right,
- top,
- // x3, y3
- right,
- bottom,
- // x4, y4
- left,
- bottom,
- // x5, y5
- left,
- top,
- ),
- centerX = (left + right) / 2,
- centerY = (top + bottom) / 2,
+ RoundedPolygon.rectangle(
+ width = right - left,
+ height = bottom - top,
+ centerX = (right - left) / 2,
+ centerY = (bottom - top) / 2,
rounding = CornerRounding(cornerR),
)
}
diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
index d59fc19..6f1d98f 100644
--- a/src/com/android/launcher3/graphics/SysUiScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -40,6 +40,7 @@
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
/**
* View scrim which draws behind hotseat and workspace
@@ -94,8 +95,8 @@
public SysUiScrim(View view) {
mRoot = view;
- mContainer = StatefulContainer.fromContext(view.getContext());
- DisplayMetrics dm = mContainer.getContext().getResources().getDisplayMetrics();
+ mContainer = ActivityContext.lookupContext(view.getContext());
+ DisplayMetrics dm = mContainer.asContext().getResources().getDisplayMetrics();
mTopMaskHeight = ResourceUtils.pxFromDp(TOP_MASK_HEIGHT_DP, dm);
mBottomMaskHeight = ResourceUtils.pxFromDp(BOTTOM_MASK_HEIGHT_DP, dm);
@@ -173,12 +174,12 @@
@Override
public void onViewAttachedToWindow(View view) {
- ScreenOnTracker.INSTANCE.get(mContainer.getContext()).addListener(mScreenOnListener);
+ ScreenOnTracker.INSTANCE.get(mContainer.asContext()).addListener(mScreenOnListener);
}
@Override
public void onViewDetachedFromWindow(View view) {
- ScreenOnTracker.INSTANCE.get(mContainer.getContext()).removeListener(mScreenOnListener);
+ ScreenOnTracker.INSTANCE.get(mContainer.asContext()).removeListener(mScreenOnListener);
}
/**
@@ -213,7 +214,7 @@
}
private Bitmap createDitheredAlphaMask(int height, @ColorInt int[] colors, float[] positions) {
- DisplayMetrics dm = mContainer.getContext().getResources().getDisplayMetrics();
+ DisplayMetrics dm = mContainer.asContext().getResources().getDisplayMetrics();
int width = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_WIDTH_DP, dm);
Bitmap dst = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(dst);
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 59fff62..5c6debe 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -16,7 +16,12 @@
package com.android.launcher3.icons;
+import static android.graphics.Color.BLACK;
+
+import static com.android.launcher3.graphics.ThemeManager.PREF_ICON_SHAPE;
+
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
@@ -26,6 +31,7 @@
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.pm.UserCache;
@@ -41,6 +47,12 @@
*/
public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
+ private static final float SEVEN_SIDED_COOKIE_SCALE = 72f / 80f;
+ private static final float FOUR_SIDED_COOKIE_SCALE = 72f / 83.4f;
+ private static final float VERY_SUNNY_SCALE = 72f / 92f;
+ private static final float DEFAULT_ICON_SCALE = 1f;
+
+
private static final MainThreadInitializedObject<Pool> POOL =
new MainThreadInitializedObject<>(Pool::new);
@@ -87,6 +99,36 @@
}
@Override
+ protected void drawAdaptiveIcon(
+ @NonNull Canvas canvas,
+ @NonNull AdaptiveIconDrawable drawable,
+ @NonNull Path overridePath
+ ) {
+ if (!Flags.enableLauncherIconShapes()) {
+ super.drawAdaptiveIcon(canvas, drawable, overridePath);
+ return;
+ }
+ String shapeKey = LauncherPrefs.get(mContext).get(PREF_ICON_SHAPE);
+ float iconScale = switch (shapeKey) {
+ case "seven_sided_cookie" -> SEVEN_SIDED_COOKIE_SCALE;
+ case "four_sided_cookie" -> FOUR_SIDED_COOKIE_SCALE;
+ case "sunny" -> VERY_SUNNY_SCALE;
+ default -> DEFAULT_ICON_SCALE;
+ };
+ canvas.clipPath(overridePath);
+ canvas.drawColor(BLACK);
+ canvas.save();
+ canvas.scale(iconScale, iconScale, canvas.getWidth() / 2f, canvas.getHeight() / 2f);
+ if (drawable.getBackground() != null) {
+ drawable.getBackground().draw(canvas);
+ }
+ if (drawable.getForeground() != null) {
+ drawable.getForeground().draw(canvas);
+ }
+ canvas.restore();
+ }
+
+ @Override
public void close() {
recycle();
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2d30466..9a1c874 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -859,6 +859,18 @@
@UiEvent(doc = "User sets the device in Fixed Landscape")
FIXED_LANDSCAPE_TOGGLE_DISABLED(2020),
+
+ @UiEvent(doc = "Work utility view expand animation started")
+ LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_BEGIN(2075),
+
+ @UiEvent(doc = "Work utility view expand animation ended")
+ LAUNCHER_WORK_UTILITY_VIEW_EXPAND_ANIMATION_END(2076),
+
+ @UiEvent(doc = "Work utility view shrink animation started")
+ LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_BEGIN(2077),
+
+ @UiEvent(doc = "Work utility view shrink animation ended")
+ LAUNCHER_WORK_UTILITY_VIEW_SHRINK_ANIMATION_END(2078),
// ADD MORE
;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 3ee029b..c1ee69b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -853,8 +853,6 @@
+ ", isArchived: " + activityInfo.getApplicationInfo().isArchived);
}
}
- logASplit("Loading IconRequestInfo without iconBlob for AppInfo: "
- + appInfo.getTargetComponent());
return new IconRequestInfo<>(appInfo, activityInfo, false /* useLowResIcon= */);
}
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 97b62b4..fe8fb5f 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -215,8 +215,7 @@
PackageManagerHelper.getLoadingProgress(lai),
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
info.setNonResizeable(apiWrapper.isNonResizeableActivity(lai));
- info.setSupportsMultiInstance(
- pmHelper.supportsMultiInstance(lai.getComponentName()));
+ info.setSupportsMultiInstance(apiWrapper.supportsMultiInstance(lai));
return (oldProgressLevel != info.getProgressLevel())
|| (oldRuntimeStatusFlags != info.runtimeStatusFlags);
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 1c9db17..e52ca6d 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -40,7 +40,6 @@
import androidx.annotation.LayoutRes;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -579,7 +578,7 @@
/**
* Dismisses the popup if it is no longer valid
*/
- public static void dismissInvalidPopup(BaseDraggingActivity activity) {
+ public static <T extends Context & ActivityContext> void dismissInvalidPopup(T activity) {
PopupContainerWithArrow popup = getOpen(activity);
if (popup != null && (!popup.mOriginalIcon.isAttachedToWindow()
|| !ShortcutUtil.supportsShortcuts((ItemInfo) popup.mOriginalIcon.getTag()))) {
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 9b3292d..df27b54 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.secondarydisplay;
+import static com.android.launcher3.util.WallpaperThemeManager.setWallpaperDependentTheme;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
@@ -29,7 +31,7 @@
import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@@ -59,7 +61,6 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.HashMap;
import java.util.Map;
@@ -67,7 +68,7 @@
/**
* Launcher activity for secondary displays
*/
-public class SecondaryDisplayLauncher extends BaseDraggingActivity
+public class SecondaryDisplayLauncher extends BaseActivity
implements BgDataModel.Callbacks, DragController.DragListener {
private LauncherModel mModel;
@@ -77,7 +78,6 @@
private View mAppsButton;
private PopupDataProvider mPopupDataProvider;
- private WidgetPickerDataProvider mWidgetPickerDataProvider;
private boolean mAppDrawerShown = false;
@@ -90,6 +90,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setWallpaperDependentTheme(this);
mModel = LauncherAppState.getInstance(this).getModel();
mDragController = new SecondaryDragController(this);
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
@@ -202,14 +203,6 @@
}
@Override
- public View getRootView() {
- return mDragLayer;
- }
-
- @Override
- protected void reapplyUi() { }
-
- @Override
public BaseDragLayer getDragLayer() {
return mDragLayer;
}
@@ -317,11 +310,6 @@
}
@Override
- public WidgetPickerDataProvider getWidgetPickerDataProvider() {
- return mWidgetPickerDataProvider;
- }
-
- @Override
public OnClickListener getItemOnClickListener() {
return this::onIconClicked;
}
diff --git a/src/com/android/launcher3/shapes/ShapesProvider.kt b/src/com/android/launcher3/shapes/ShapesProvider.kt
index dfb7793..f1ea3a0 100644
--- a/src/com/android/launcher3/shapes/ShapesProvider.kt
+++ b/src/com/android/launcher3/shapes/ShapesProvider.kt
@@ -202,9 +202,9 @@
)
} else {
mapOf(
- "default" to
+ "circle" to
IconShapeModel(
- key = "default",
+ key = "circle",
title = "circle",
pathString = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0",
)
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index f6b610c..b7dd2bf 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -15,15 +15,13 @@
*/
package com.android.launcher3.statemanager;
-import android.content.Context;
-
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.views.ActivityContext;
/**
* Interface representing a state of a StatefulContainer
*/
-public interface BaseState<T extends BaseState> {
+public interface BaseState<T> {
// Flag to indicate that Launcher is non-interactive in this state
int FLAG_NON_INTERACTIVE = 1 << 0;
@@ -37,8 +35,7 @@
/**
* @return How long the animation to this state should take (or from this state to NORMAL).
*/
- <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
- int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState);
+ int getTransitionDuration(ActivityContext context, boolean isToState);
/**
* Returns the state to go back to from this state
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 763f3ba..a125331 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -27,7 +27,6 @@
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
@@ -50,50 +49,49 @@
/**
* Class to manage transitions between different states for a StatefulActivity based on different
* states
- * @param STATE_TYPE Basestate used by the state manager
- * @param STATEFUL_CONTAINER container object used to manage state
+ * @param <S> Basestate used by the state manager
+ * @param <T> container object used to manage state
*/
-public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>,
- STATEFUL_CONTAINER extends Context & StatefulContainer<STATE_TYPE>> {
+public class StateManager<S extends BaseState<S>, T extends StatefulContainer<S>> {
public static final String TAG = "StateManager";
// b/279059025, b/325463989
private static final boolean DEBUG = true;
- private final AnimationState mConfig = new AnimationState();
+ private final AnimationState<S> mConfig = new AnimationState<>();
private final Handler mUiHandler;
- private final STATEFUL_CONTAINER mStatefulContainer;
- private final ArrayList<StateListener<STATE_TYPE>> mListeners = new ArrayList<>();
- private final STATE_TYPE mBaseState;
+ private final T mContainer;
+ private final ArrayList<StateListener<S>> mListeners = new ArrayList<>();
+ private final S mBaseState;
// Animators which are run on properties also controlled by state animations.
- private final AtomicAnimationFactory mAtomicAnimationFactory;
+ private final AtomicAnimationFactory<S> mAtomicAnimationFactory;
- private StateHandler<STATE_TYPE>[] mStateHandlers;
- private STATE_TYPE mState;
+ private StateHandler<S>[] mStateHandlers;
+ private S mState;
- private STATE_TYPE mLastStableState;
- private STATE_TYPE mCurrentStableState;
+ private S mLastStableState;
+ private S mCurrentStableState;
- private STATE_TYPE mRestState;
+ private S mRestState;
- public StateManager(STATEFUL_CONTAINER container, STATE_TYPE baseState) {
+ public StateManager(T container, S baseState) {
mUiHandler = new Handler(Looper.getMainLooper());
- mStatefulContainer = container;
+ mContainer = container;
mBaseState = baseState;
mState = mLastStableState = mCurrentStableState = baseState;
mAtomicAnimationFactory = container.createAtomicAnimationFactory();
}
- public STATE_TYPE getState() {
+ public S getState() {
return mState;
}
- public STATE_TYPE getTargetState() {
- return (STATE_TYPE) mConfig.targetState;
+ public S getTargetState() {
+ return mConfig.targetState;
}
- public STATE_TYPE getCurrentStableState() {
+ public S getCurrentStableState() {
return mCurrentStableState;
}
@@ -115,20 +113,20 @@
writer.println(prefix + "\tisInTransition:" + isInTransition());
}
- public StateHandler<STATE_TYPE>[] getStateHandlers() {
+ public StateHandler<S>[] getStateHandlers() {
if (mStateHandlers == null) {
- ArrayList<StateHandler<STATE_TYPE>> handlers = new ArrayList<>();
- mStatefulContainer.collectStateHandlers(handlers);
+ ArrayList<StateHandler<S>> handlers = new ArrayList<>();
+ mContainer.collectStateHandlers(handlers);
mStateHandlers = handlers.toArray(new StateHandler[handlers.size()]);
}
return mStateHandlers;
}
- public void addStateListener(StateListener listener) {
+ public void addStateListener(StateListener<S> listener) {
mListeners.add(listener);
}
- public void removeStateListener(StateListener listener) {
+ public void removeStateListener(StateListener<S> listener) {
mListeners.remove(listener);
}
@@ -136,14 +134,14 @@
* Returns true if the state changes should be animated.
*/
public boolean shouldAnimateStateChange() {
- return mStatefulContainer.shouldAnimateStateChange();
+ return mContainer.shouldAnimateStateChange();
}
/**
* @return {@code true} if the state matches the current state and there is no active
* transition to different state.
*/
- public boolean isInStableState(STATE_TYPE state) {
+ public boolean isInStableState(S state) {
return mState == state && mCurrentStableState == state
&& (mConfig.targetState == null || mConfig.targetState == state);
}
@@ -156,23 +154,23 @@
}
/**
- * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
+ * @see #goToState(S, boolean, AnimatorListener)
*/
- public void goToState(STATE_TYPE state) {
+ public void goToState(S state) {
goToState(state, shouldAnimateStateChange());
}
/**
- * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
+ * @see #goToState(S, boolean, AnimatorListener)
*/
- public void goToState(STATE_TYPE state, AnimatorListener listener) {
+ public void goToState(S state, AnimatorListener listener) {
goToState(state, shouldAnimateStateChange(), listener);
}
/**
- * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
+ * @see #goToState(S, boolean, AnimatorListener)
*/
- public void goToState(STATE_TYPE state, boolean animated) {
+ public void goToState(S state, boolean animated) {
goToState(state, animated, 0, null);
}
@@ -183,21 +181,21 @@
* true otherwise
* @param listener any action to perform at the end of the transition, or null.
*/
- public void goToState(STATE_TYPE state, boolean animated, AnimatorListener listener) {
+ public void goToState(S state, boolean animated, AnimatorListener listener) {
goToState(state, animated, 0, listener);
}
/**
* Changes the Launcher state to the provided state after the given delay.
*/
- public void goToState(STATE_TYPE state, long delay, AnimatorListener listener) {
+ public void goToState(S state, long delay, AnimatorListener listener) {
goToState(state, true, delay, listener);
}
/**
* Changes the Launcher state to the provided state after the given delay.
*/
- public void goToState(STATE_TYPE state, long delay) {
+ public void goToState(S state, long delay) {
goToState(state, true, delay, null);
}
@@ -219,7 +217,7 @@
cancelAnimation();
}
if (mConfig.currentAnimation == null) {
- for (StateHandler handler : getStateHandlers()) {
+ for (StateHandler<S> handler : getStateHandlers()) {
handler.setState(mState);
}
if (wasInAnimation) {
@@ -230,21 +228,21 @@
/** Handles backProgress in predictive back gesture by passing it to state handlers. */
public void onBackProgressed(
- STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
- for (StateHandler handler : getStateHandlers()) {
+ S toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ for (StateHandler<S> handler : getStateHandlers()) {
handler.onBackProgressed(toState, backProgress);
}
}
/** Handles back cancelled event in predictive back gesture by passing it to state handlers. */
- public void onBackCancelled(STATE_TYPE toState) {
- for (StateHandler handler : getStateHandlers()) {
+ public void onBackCancelled(S toState) {
+ for (StateHandler<S> handler : getStateHandlers()) {
handler.onBackCancelled(toState);
}
}
private void goToState(
- STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
+ S state, boolean animated, long delay, AnimatorListener listener) {
if (enableStateManagerProtoLog()) {
StateManagerProtoLogProxy.logGoToState(
mState, state, getTrimmedStackTrace("StateManager.goToState"));
@@ -254,7 +252,7 @@
}
animated &= areAnimatorsEnabled();
- if (mStatefulContainer.isInState(state)) {
+ if (getState() == state) {
if (mConfig.currentAnimation == null) {
// Run any queued runnable
if (listener != null) {
@@ -273,13 +271,13 @@
}
// Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
- STATE_TYPE fromState = mState;
+ S fromState = mState;
cancelAnimation();
if (!animated) {
mAtomicAnimationFactory.cancelAllStateElementAnimation();
onStateTransitionStart(state);
- for (StateHandler handler : getStateHandlers()) {
+ for (StateHandler<S> handler : getStateHandlers()) {
handler.setState(state);
}
@@ -306,13 +304,13 @@
}
}
- private void goToStateAnimated(STATE_TYPE state, STATE_TYPE fromState,
+ private void goToStateAnimated(S state, S fromState,
AnimatorListener listener) {
// Since state mBaseState can be reached from multiple states, just assume that the
// transition plays in reverse and use the same duration as previous state.
mConfig.duration = state == mBaseState
- ? fromState.getTransitionDuration(mStatefulContainer, false /* isToState */)
- : state.getTransitionDuration(mStatefulContainer, true /* isToState */);
+ ? fromState.getTransitionDuration(mContainer, false /* isToState */)
+ : state.getTransitionDuration(mContainer, true /* isToState */);
prepareForAtomicAnimation(fromState, state, mConfig);
AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
if (listener != null) {
@@ -326,7 +324,7 @@
* - Setting interpolators for various animations included in the state transition.
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/
- public void prepareForAtomicAnimation(STATE_TYPE fromState, STATE_TYPE toState,
+ public void prepareForAtomicAnimation(S fromState, S toState,
StateAnimationConfig config) {
mAtomicAnimationFactory.prepareForAtomicAnimation(fromState, toState, config);
}
@@ -335,7 +333,7 @@
* Creates an animation representing atomic transitions between the provided states
*/
public AnimatorSet createAtomicAnimation(
- STATE_TYPE fromState, STATE_TYPE toState, StateAnimationConfig config) {
+ S fromState, S toState, StateAnimationConfig config) {
if (enableStateManagerProtoLog()) {
StateManagerProtoLogProxy.logCreateAtomicAnimation(
mState, toState, getTrimmedStackTrace("StateManager.createAtomicAnimation"));
@@ -348,7 +346,7 @@
PendingAnimation builder = new PendingAnimation(config.duration);
prepareForAtomicAnimation(fromState, toState, config);
- for (StateHandler handler : mStatefulContainer.getStateManager().getStateHandlers()) {
+ for (StateHandler<S> handler : getStateHandlers()) {
handler.setStateWithAnimation(toState, config, builder);
}
return builder.buildAnim();
@@ -362,19 +360,19 @@
* accuracy.
*/
public AnimatorPlaybackController createAnimationToNewWorkspace(
- STATE_TYPE state, long duration) {
+ S state, long duration) {
return createAnimationToNewWorkspace(state, duration, 0 /* animFlags */);
}
public AnimatorPlaybackController createAnimationToNewWorkspace(
- STATE_TYPE state, long duration, @AnimationFlags int animFlags) {
+ S state, long duration, @AnimationFlags int animFlags) {
StateAnimationConfig config = new StateAnimationConfig();
config.duration = duration;
config.animFlags = animFlags;
return createAnimationToNewWorkspace(state, config);
}
- public AnimatorPlaybackController createAnimationToNewWorkspace(STATE_TYPE state,
+ public AnimatorPlaybackController createAnimationToNewWorkspace(S state,
StateAnimationConfig config) {
config.animProps |= StateAnimationConfig.USER_CONTROLLED;
cancelAnimation();
@@ -384,10 +382,10 @@
return mConfig.playbackController;
}
- private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
+ private PendingAnimation createAnimationToNewWorkspaceInternal(final S state) {
PendingAnimation builder = new PendingAnimation(mConfig.duration);
if (!mConfig.hasAnimationFlag(SKIP_ALL_ANIMATIONS)) {
- for (StateHandler handler : getStateHandlers()) {
+ for (StateHandler<S> handler : getStateHandlers()) {
handler.setStateWithAnimation(state, mConfig, builder);
}
}
@@ -396,7 +394,7 @@
return builder;
}
- private AnimatorListener createStateAnimationListener(STATE_TYPE state) {
+ private AnimatorListener createStateAnimationListener(S state) {
return new AnimationSuccessListener() {
@Override
@@ -412,9 +410,9 @@
};
}
- private void onStateTransitionStart(STATE_TYPE state) {
+ private void onStateTransitionStart(S state) {
mState = state;
- mStatefulContainer.onStateSetStart(mState);
+ mContainer.onStateSetStart(mState);
if (enableStateManagerProtoLog()) {
StateManagerProtoLogProxy.logOnStateTransitionStart(state);
@@ -426,14 +424,14 @@
}
}
- private void onStateTransitionEnd(STATE_TYPE state) {
+ private void onStateTransitionEnd(S state) {
// Only change the stable states after the transitions have finished
if (state != mCurrentStableState) {
mLastStableState = state.getHistoryForState(mCurrentStableState);
mCurrentStableState = state;
}
- mStatefulContainer.onStateSetEnd(state);
+ mContainer.onStateSetEnd(state);
if (state == mBaseState) {
setRestState(null);
}
@@ -448,7 +446,7 @@
}
}
- public STATE_TYPE getLastState() {
+ public S getLastState() {
return mLastStableState;
}
@@ -468,11 +466,11 @@
}
}
- public STATE_TYPE getRestState() {
+ public S getRestState() {
return mRestState == null ? mBaseState : mRestState;
}
- public void setRestState(STATE_TYPE restState) {
+ public void setRestState(S restState) {
mRestState = restState;
}
@@ -518,7 +516,7 @@
* @param anim The custom animation to the given state.
* @param toState The state we are animating towards.
*/
- public void setCurrentAnimation(AnimatorSet anim, STATE_TYPE toState) {
+ public void setCurrentAnimation(AnimatorSet anim, S toState) {
cancelAnimation();
setCurrentAnimation(anim);
anim.addListener(createStateAnimationListener(toState));
@@ -691,10 +689,10 @@
/** Handles backProgress in predictive back gesture for target state. */
default void onBackProgressed(
- STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {};
+ STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {}
/** Handles back cancelled event in predictive back gesture for target state. */
- default void onBackCancelled(STATE_TYPE toState) {};
+ default void onBackCancelled(STATE_TYPE toState) {}
}
public interface StateListener<STATE_TYPE> {
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index f21e5da..445701d 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -18,10 +18,8 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_RECREATE_TO_UPDATE_THEME;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
-import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
@@ -30,9 +28,8 @@
import android.view.View;
import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.LauncherRootView;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -46,7 +43,7 @@
* @param <STATE_TYPE> Type of state object
*/
public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
- extends BaseDraggingActivity implements StatefulContainer<STATE_TYPE> {
+ extends BaseActivity implements StatefulContainer<STATE_TYPE> {
public final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@@ -56,7 +53,6 @@
protected Configuration mOldConfig;
private int mOldRotation;
- private boolean mRecreateToUpdateTheme = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -66,18 +62,6 @@
mOldRotation = WindowManagerProxy.INSTANCE.get(this).getRotation(this);
}
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putBoolean(RUNTIME_STATE_RECREATE_TO_UPDATE_THEME, mRecreateToUpdateTheme);
- super.onSaveInstanceState(outState);
- }
-
- @Override
- protected void recreateToUpdateTheme() {
- mRecreateToUpdateTheme = true;
- super.recreateToUpdateTheme();
- }
-
/**
* Create handlers to control the property changes for this activity
*/
@@ -214,11 +198,6 @@
mOldRotation = rotation;
}
- @Override
- public Context getContext() {
- return this;
- }
-
/**
* Logic for when device configuration changes (rotation, screen size change, multi-window,
* etc.)
diff --git a/src/com/android/launcher3/statemanager/StatefulContainer.java b/src/com/android/launcher3/statemanager/StatefulContainer.java
index b10af0a..83a2fdc 100644
--- a/src/com/android/launcher3/statemanager/StatefulContainer.java
+++ b/src/com/android/launcher3/statemanager/StatefulContainer.java
@@ -20,8 +20,6 @@
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
-import android.content.Context;
-import android.content.ContextWrapper;
import android.content.res.Configuration;
import androidx.annotation.CallSuper;
@@ -40,23 +38,6 @@
ActivityContext {
/**
- * Returns an instance of an implementation of StatefulContainer
- *
- * @param context will find instance of StatefulContainer from given context.
- */
- static <T extends StatefulContainer> T fromContext(Context context) {
- if (context instanceof StatefulContainer) {
- return (T) context;
- } else if (context instanceof ContextWrapper) {
- return fromContext(((ContextWrapper) context).getBaseContext());
- } else {
- throw new IllegalArgumentException("Cannot find StatefulContainer in parent tree");
- }
- }
-
- Context getContext();
-
- /**
* Creates a factory for atomic state animations
*/
default StateManager.AtomicAnimationFactory<STATE_TYPE> createAtomicAnimationFactory() {
diff --git a/src/com/android/launcher3/states/EditModeState.kt b/src/com/android/launcher3/states/EditModeState.kt
index 6ff47ae..268a373 100644
--- a/src/com/android/launcher3/states/EditModeState.kt
+++ b/src/com/android/launcher3/states/EditModeState.kt
@@ -36,11 +36,7 @@
FLAG_WORKSPACE_HAS_BACKGROUNDS)
}
- override fun <T> getTransitionDuration(context: T, isToState: Boolean): Int where
- T : Context?,
- T : ActivityContext? {
- return 150
- }
+ override fun getTransitionDuration(context: ActivityContext, isToState: Boolean) = 150
override fun <T> getDepthUnchecked(context: T): Float where T : Context?, T : ActivityContext? {
if (enableScalingRevealHomeAnimation()) {
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index bf2fb30..ed22d39 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -26,6 +26,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
/**
* Scale down workspace/hotseat to hint at going to either overview (on pause) or first home screen.
@@ -46,7 +47,7 @@
}
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
return 80;
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 2e57ed8..15e6c61 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -24,6 +24,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
+import com.android.launcher3.views.ActivityContext;
/**
* Definition for spring loaded state used during drag and drop.
@@ -41,7 +42,7 @@
}
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
return 150;
}
diff --git a/src/com/android/launcher3/util/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java
index 48e033a..56337b0 100644
--- a/src/com/android/launcher3/util/ApiWrapper.java
+++ b/src/com/android/launcher3/util/ApiWrapper.java
@@ -59,10 +59,13 @@
LauncherAppComponent::getApiWrapper);
protected final Context mContext;
+ private final String[] mLegacyMultiInstanceSupportedApps;
@Inject
public ApiWrapper(@ApplicationContext Context context) {
mContext = context;
+ mLegacyMultiInstanceSupportedApps = context.getResources().getStringArray(
+ com.android.launcher3.R.array.config_appsSupportMultiInstancesSplit);
}
/**
@@ -157,12 +160,31 @@
/**
* Checks if an activity is flagged as non-resizeable.
*/
- public boolean isNonResizeableActivity(LauncherActivityInfo lai) {
- // Overridden in quickstep
+ public boolean isNonResizeableActivity(@NonNull LauncherActivityInfo lai) {
+ // Overridden in Quickstep
return false;
}
/**
+ * Checks if an activity supports multi-instance.
+ */
+ public boolean supportsMultiInstance(@NonNull LauncherActivityInfo lai) {
+ // Check app multi-instance properties after V
+ if (!Utilities.ATLEAST_V) {
+ return false;
+ }
+
+ // Check the legacy hardcoded allowlist first
+ for (String pkg : mLegacyMultiInstanceSupportedApps) {
+ if (pkg.equals(lai.getComponentName().getPackageName())) {
+ return true;
+ }
+ }
+
+ // Overridden in Quickstep
+ return false;
+ }
+ /**
* Starts an Activity which can be used to set this Launcher as the HOME app, via a consent
* screen. In case the consent screen cannot be shown, or the user does not set current Launcher
* as HOME app, a toast asking the user to do the latter is shown.
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 4b60d98..3d01f24 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,8 +16,6 @@
package com.android.launcher3.util;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
-
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
import android.content.ActivityNotFoundException;
@@ -41,7 +39,6 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
@@ -77,15 +74,11 @@
@NonNull
private final LauncherApps mLauncherApps;
- private final String[] mLegacyMultiInstanceSupportedApps;
-
@Inject
public PackageManagerHelper(@ApplicationContext final Context context) {
mContext = context;
mPm = context.getPackageManager();
mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
- mLegacyMultiInstanceSupportedApps = mContext.getResources().getStringArray(
- R.array.config_appsSupportMultiInstancesSplit);
}
/**
@@ -193,39 +186,6 @@
}
/**
- * Returns whether the given component or its application has the multi-instance property set.
- */
- public boolean supportsMultiInstance(@NonNull ComponentName component) {
- // Check the legacy hardcoded allowlist first
- for (String pkg : mLegacyMultiInstanceSupportedApps) {
- if (pkg.equals(component.getPackageName())) {
- return true;
- }
- }
-
- // Check app multi-instance properties after V
- if (!Utilities.ATLEAST_V) {
- return false;
- }
-
- try {
- // Check if the component has the multi-instance property
- return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, component)
- .getBoolean();
- } catch (PackageManager.NameNotFoundException e1) {
- try {
- // Check if the application has the multi-instance property
- return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI,
- component.getPackageName())
- .getBoolean();
- } catch (PackageManager.NameNotFoundException e2) {
- // Fall through
- }
- }
- return false;
- }
-
- /**
* Returns whether two apps should be considered the same for multi-instance purposes, which
* requires additional checks to ensure they can be started as multiple instances.
*/
diff --git a/src/com/android/launcher3/util/WallpaperThemeManager.kt b/src/com/android/launcher3/util/WallpaperThemeManager.kt
new file mode 100644
index 0000000..c48ef07
--- /dev/null
+++ b/src/com/android/launcher3/util/WallpaperThemeManager.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.app.Activity
+import android.content.ComponentCallbacks
+import android.content.res.Configuration
+import android.os.Bundle
+import com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE_RECREATE_TO_UPDATE_THEME
+import com.android.launcher3.R
+
+/** Utility class to manage activity's theme in case it is wallpaper dependent */
+class WallpaperThemeManager private constructor(private val activity: Activity) :
+ OnColorHintListener, ActivityLifecycleCallbacksAdapter, ComponentCallbacks {
+
+ private var themeRes: Int = R.style.AppTheme
+
+ private var recreateToUpdateTheme = false
+
+ init {
+ // Update theme
+ WallpaperColorHints.get(activity).registerOnColorHintsChangedListener(this)
+ val expectedTheme = Themes.getActivityThemeRes(activity)
+ if (expectedTheme != themeRes) {
+ themeRes = expectedTheme
+ activity.setTheme(expectedTheme)
+ }
+
+ activity.registerActivityLifecycleCallbacks(this)
+ activity.registerComponentCallbacks(this)
+ }
+
+ override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) =
+ bundle.putBoolean(RUNTIME_STATE_RECREATE_TO_UPDATE_THEME, recreateToUpdateTheme)
+
+ override fun onActivityDestroyed(unused: Activity) =
+ WallpaperColorHints.get(activity).unregisterOnColorsChangedListener(this)
+
+ override fun onConfigurationChanged(config: Configuration) = updateTheme()
+
+ override fun onLowMemory() {}
+
+ override fun onColorHintsChanged(colorHints: Int) = updateTheme()
+
+ private fun updateTheme() {
+ if (themeRes != Themes.getActivityThemeRes(activity)) {
+ recreateToUpdateTheme = true
+ activity.recreate()
+ }
+ }
+
+ companion object {
+
+ /**
+ * Sets a wallpaper dependent theme on this activity. The activity is automatically
+ * recreated when a wallpaper change can potentially change the theme.
+ */
+ @JvmStatic
+ fun Activity.setWallpaperDependentTheme() {
+ if (!isDestroyed) {
+ WallpaperThemeManager(this)
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/rects/Rects.kt b/src/com/android/launcher3/util/rects/Rects.kt
index 1e6d717..2f1942a 100644
--- a/src/com/android/launcher3/util/rects/Rects.kt
+++ b/src/com/android/launcher3/util/rects/Rects.kt
@@ -18,6 +18,24 @@
import android.graphics.Rect
import android.view.View
+import com.android.launcher3.Utilities
+
+/**
+ * Linearly interpolate between two rectangles. The result is stored in the rect the function is
+ * called on.
+ *
+ * @param start the starting rectangle
+ * @param end the ending rectangle
+ * @param t the interpolation factor, where 0 is the start and 1 is the end
+ */
+fun Rect.lerpRect(start: Rect, end: Rect, t: Float) {
+ set(
+ Utilities.mapRange(t, start.left.toFloat(), end.left.toFloat()).toInt(),
+ Utilities.mapRange(t, start.top.toFloat(), end.top.toFloat()).toInt(),
+ Utilities.mapRange(t, start.right.toFloat(), end.right.toFloat()).toInt(),
+ Utilities.mapRange(t, start.bottom.toFloat(), end.bottom.toFloat()).toInt(),
+ )
+}
/** Copy the coordinates of the [view] relative to its parent into this rectangle. */
fun Rect.set(view: View) {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index b164b7f..81968fc 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -62,6 +62,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DropTargetHandler;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -166,6 +167,11 @@
return false;
}
+ /** Returns the RootView */
+ default View getRootView() {
+ return getDragLayer();
+ }
+
/**
* The root view to support drag-and-drop and popup support.
*/
@@ -410,7 +416,8 @@
View v, Intent intent, @Nullable ItemInfo item) {
Preconditions.assertUIThread();
Context context = (Context) this;
- if (isAppBlockedForSafeMode() && !new ApplicationInfoWrapper(context, intent).isSystem()) {
+ if (LauncherAppState.getInstance(context).isSafeModeEnabled()
+ && !new ApplicationInfoWrapper(context, intent).isSystem()) {
Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return null;
}
@@ -456,11 +463,6 @@
return null;
}
- /** Returns {@code true} if an app launch is blocked due to safe mode. */
- default boolean isAppBlockedForSafeMode() {
- return false;
- }
-
/**
* Creates and logs a new app launch event.
*/
@@ -476,6 +478,7 @@
* @param v View initiating a launch.
* @param item Item associated with the view.
*/
+ @NonNull
default ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
@@ -525,6 +528,11 @@
return false;
}
+ /** Returns the current ActivityContext as context */
+ default Context asContext() {
+ return (Context) this;
+ }
+
/**
* Returns the ActivityContext associated with the given Context, or throws an exception if
* the Context is not associated with any ActivityContext.
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index f499fca..78197e2 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -41,7 +41,6 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
@@ -50,6 +49,7 @@
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
@@ -218,8 +218,7 @@
* @param widgetId The ID of the widget
* @param requestCode The request code
*/
- public void startConfigActivity(@NonNull BaseDraggingActivity activity, int widgetId,
- int requestCode) {
+ public void startConfigActivity(@NonNull BaseActivity activity, int widgetId, int requestCode) {
if (!WIDGETS_ENABLED) {
sendActionCancelled(activity, requestCode);
return;
@@ -245,7 +244,7 @@
* the configuration of the {@code widgetId} app widget, or null of options cannot be produced.
*/
@Nullable
- protected Bundle getConfigurationActivityOptions(@NonNull BaseDraggingActivity activity,
+ protected Bundle getConfigurationActivityOptions(@NonNull ActivityContext activity,
int widgetId) {
LauncherAppWidgetHostView view = mViews.get(widgetId);
if (view == null) {
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
index 9865516..03a5535 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -18,8 +18,6 @@
import static com.android.app.animation.Interpolators.DECELERATE;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
-import android.content.Context;
-
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -31,8 +29,6 @@
*/
public class AllAppsState extends LauncherState {
- private static final float PARALLAX_COEFFICIENT = .125f;
-
private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE;
public AllAppsState(int id) {
@@ -40,8 +36,7 @@
}
@Override
- public <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
- int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
return isToState
? context.getDeviceProfile().allAppsOpenDuration
: context.getDeviceProfile().allAppsCloseDuration;
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
index 7a228c4..532a338 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,12 +17,11 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
-import android.content.Context;
-
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
/**
* Definition for overview state
@@ -34,7 +33,7 @@
}
@Override
- public int getTransitionDuration(Context context, boolean isToState) {
+ public int getTransitionDuration(ActivityContext context, boolean isToState) {
return 250;
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
index 7573d2f..5e1e548 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
@@ -35,6 +35,5 @@
MODE_PRIVATE,
)
- override val Item.sharedPrefs: SharedPreferences
- get() = backingPrefs
+ override fun getSharedPrefs(item: Item): SharedPreferences = backingPrefs
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/ProxyPrefsTest.kt b/tests/multivalentTests/src/com/android/launcher3/ProxyPrefsTest.kt
new file mode 100644
index 0000000..54f6f63
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/ProxyPrefsTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2025 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
+
+import android.content.Context.MODE_PRIVATE
+import android.platform.uiautomatorhelpers.DeviceHelpers.context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.LauncherPrefs.Companion.backedUpItem
+import com.google.common.truth.Truth.assertThat
+import java.util.UUID
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ProxyPrefsTest {
+
+ private val prefName = "pref-test-" + UUID.randomUUID().toString()
+
+ private val proxyPrefs by lazy {
+ ProxyPrefs(
+ context,
+ context.getSharedPreferences(prefName, MODE_PRIVATE).apply { edit().clear().commit() },
+ )
+ }
+ private val launcherPrefs by lazy { LauncherPrefs(context) }
+
+ @After
+ fun tearDown() {
+ context.deleteSharedPreferences(prefName)
+ }
+
+ @Test
+ fun `returns fallback value if present`() {
+ launcherPrefs.putSync(TEST_ENTRY.to("new_value"))
+ assertThat(proxyPrefs.get(TEST_ENTRY)).isEqualTo("new_value")
+ }
+
+ @Test
+ fun `returns default value if not present`() {
+ launcherPrefs.removeSync(TEST_ENTRY)
+ assertThat(proxyPrefs.get(TEST_ENTRY)).isEqualTo("default_value")
+ }
+
+ @Test
+ fun `returns overridden value if present`() {
+ launcherPrefs.putSync(TEST_ENTRY.to("new_value"))
+ proxyPrefs.putSync(TEST_ENTRY.to("overridden_value"))
+ assertThat(proxyPrefs.get(TEST_ENTRY)).isEqualTo("overridden_value")
+ }
+
+ @Test
+ fun `value not present when removed`() {
+ launcherPrefs.putSync(TEST_ENTRY.to("new_value"))
+ proxyPrefs.removeSync(TEST_ENTRY)
+ assertThat(proxyPrefs.has(TEST_ENTRY)).isFalse()
+ }
+
+ @Test
+ fun `returns default if removed`() {
+ launcherPrefs.putSync(TEST_ENTRY.to("new_value"))
+ proxyPrefs.removeSync(TEST_ENTRY)
+ assertThat(proxyPrefs.get(TEST_ENTRY)).isEqualTo("default_value")
+ }
+
+ @Test
+ fun `value present on init`() {
+ launcherPrefs.putSync(TEST_ENTRY.to("new_value"))
+ assertThat(proxyPrefs.has(TEST_ENTRY)).isTrue()
+ }
+
+ @Test
+ fun `value absent on init`() {
+ launcherPrefs.removeSync(TEST_ENTRY)
+ assertThat(proxyPrefs.has(TEST_ENTRY)).isFalse()
+ }
+
+ companion object {
+
+ val TEST_ENTRY =
+ backedUpItem("test_prefs", "default_value", EncryptionType.DEVICE_PROTECTED)
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
index cb04e13..cab1ebe 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
@@ -116,7 +116,13 @@
*/
@ScreenRecord // b/381918059
@Test
- public void testAddAndDeletePageAndFling() {
+ public void testAddAndDeletePageAndFling() throws Exception {
+ // Set workspace that includes the chrome Activity app icon on the hotseat.
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main");
+ mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
+ reinitializeLauncherData();
+
Workspace workspace = mLauncher.getWorkspace();
// Get the first app from the hotseat
HomeAppIcon hotSeatIcon = workspace.getHotseatAppIcon(0);
diff --git a/tests/src/com/android/launcher3/util/WallpaperThemeManagerTest.kt b/tests/src/com/android/launcher3/util/WallpaperThemeManagerTest.kt
new file mode 100644
index 0000000..4c8fd8a
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/WallpaperThemeManagerTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.app.Activity
+import android.content.ComponentCallbacks
+import android.content.res.Configuration
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.any
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.eq
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.times
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.launcher3.util.WallpaperThemeManager.Companion.setWallpaperDependentTheme
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.never
+import org.mockito.kotlin.whenever
+
+/** Tests for WallpaperThemeManager */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WallpaperThemeManagerTest {
+
+ @get:Rule val context = SandboxApplication()
+
+ @Mock lateinit var activity: Activity
+ @Captor lateinit var callbacksCaptor: ArgumentCaptor<ComponentCallbacks>
+
+ private lateinit var mockSession: StaticMockitoSession
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mockSession = mockitoSession().spyStatic(Themes::class.java).startMocking()
+
+ doReturn(1).`when`<Int> { Themes.getActivityThemeRes(any()) }
+ doReturn(context).whenever(activity).applicationContext
+ }
+
+ @After
+ fun tearDown() {
+ mockSession.finishMocking()
+ }
+
+ @Test
+ fun `correct theme set on activity create`() {
+ activity.setWallpaperDependentTheme()
+ verify(activity, times(1)).setTheme(eq(1))
+ }
+
+ @Test
+ fun `ignores update if theme does not change`() {
+ activity.setWallpaperDependentTheme()
+ verify(activity).registerComponentCallbacks(callbacksCaptor.capture())
+ callbacksCaptor.value.onConfigurationChanged(Configuration())
+ verify(activity, never()).recreate()
+ }
+
+ @Test
+ fun `activity recreated if theme changes`() {
+ activity.setWallpaperDependentTheme()
+ verify(activity).registerComponentCallbacks(callbacksCaptor.capture())
+
+ doReturn(3).`when`<Int> { Themes.getActivityThemeRes(any()) }
+ callbacksCaptor.value.onConfigurationChanged(Configuration())
+ verify(activity, times(1)).recreate()
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index abf46e7..afc0dd5 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -2131,12 +2131,13 @@
downTime, currentTime, action, point.x, point.y, pointerCount,
mTrackpadGestureType)
: getMotionEvent(downTime, currentTime, action, point.x, point.y, source, toolType);
+ int button = isRightClick ? MotionEvent.BUTTON_SECONDARY : MotionEvent.BUTTON_PRIMARY;
+ if (action == MotionEvent.ACTION_BUTTON_PRESS) {
+ event.setButtonState(event.getButtonState() | button);
+ }
if (action == MotionEvent.ACTION_BUTTON_PRESS
|| action == MotionEvent.ACTION_BUTTON_RELEASE) {
- event.setActionButton(MotionEvent.BUTTON_PRIMARY);
- }
- if (isRightClick) {
- event.setButtonState(event.getButtonState() | MotionEvent.BUTTON_SECONDARY);
+ event.setActionButton(button);
}
injectEvent(event);
}
@@ -2247,11 +2248,17 @@
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN,
/* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS);
+ sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_PRESS, targetCenter,
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN,
+ /* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS);
try {
expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent);
final UiObject2 result = waitForLauncherObject(resName);
return result;
} finally {
+ sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_RELEASE, targetCenter,
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN,
+ /* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS);
sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter,
GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN,
/* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS);
@@ -2266,11 +2273,17 @@
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
/* isRightClick= */ true);
+ sendPointer(downTime, downTime, MotionEvent.ACTION_BUTTON_PRESS, targetCenter,
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
+ /* isRightClick= */ true);
try {
expectEvent(TestProtocol.SEQUENCE_MAIN, rightClickEvent);
final UiObject2 result = waitForLauncherObject(resName);
return result;
} finally {
+ sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_BUTTON_RELEASE,
+ targetCenter, GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
+ /* isRightClick= */ true);
sendPointer(downTime, SystemClock.uptimeMillis(), ACTION_UP, targetCenter,
GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
/* isRightClick= */ true);
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 4a7caf8..4230643 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -58,7 +58,7 @@
*/
public final class Workspace extends Home {
private static final int FLING_STEPS = 10;
- private static final int DEFAULT_DRAG_STEPS = 10;
+ private static final int DEFAULT_DRAG_STEPS = 15;
private static final String DROP_BAR_RES_ID = "drop_target_bar";
private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text";
@@ -682,7 +682,7 @@
launcher.movePointer(dragStart, targetDest,
DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(),
- false, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+ true, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, startsActivity);
}