Merge "Remove user TIS unlocked runnable when a TIS instance is destroyed" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index aafa1f6..ffde0d1 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -471,3 +471,10 @@
description: "Shows filtered set of widgets by default and an option to show all widgets in the widget picker"
bug: "356127021"
}
+
+flag {
+ name: "show_taskbar_pinning_popup_from_anywhere"
+ namespace: "launcher"
+ description: "Shows the pinning popup view after long-pressing or right-clicking anywhere on the pinned taskbar"
+ bug: "297325541"
+}
diff --git a/quickstep/res/drawable/ic_external_display.xml b/quickstep/res/drawable/ic_external_display.xml
new file mode 100644
index 0000000..64c183e
--- /dev/null
+++ b/quickstep/res/drawable/ic_external_display.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960">
+ <path
+ android:pathData="M320,840v-80L160,760q-33,0 -56.5,-23.5T80,680v-480q0,-33 23.5,-56.5T160,120h640q33,0 56.5,23.5T880,200v480q0,33 -23.5,56.5T800,760L640,760v80L320,840ZM160,680h640v-480L160,200v480ZM160,680v-480,480Z"
+ android:fillColor="#e8eaed"/>
+</vector>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index ed90c85..5cbe556 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Speld vas"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vormvry"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Rekenaar"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Maak almal toe"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"vou <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> uit"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"vou <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> in"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 0848ddd..ce12f9d 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ነፃ ቅጽ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ዴስክቶፕ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ሁሉንም አሰናብት"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ን ዘርጋ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ን ሰብስብ"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 501654f..0f3a854 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"تثبيت"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"شكل مجاني"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"الكمبيوتر المكتبي"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"إغلاق الكل"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"توسيع <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"تصغير <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 1dbab02..1d536e8 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"পিন"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ডেস্কটপ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"সকলো অগ্ৰাহ্য কৰক"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> বিস্তাৰ কৰক"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> সংকোচন কৰক"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index e211463..2e6337a 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Sancın"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Sərbəst rejim"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Masaüstü"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hamısını kənarlaşdırın"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"genişləndirin: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"yığcamlaşdırın: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index aa16f3c..93a8d48 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Računar"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Odbaci sve"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skupite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 4dcfe62..3f5a617 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Замацаваць"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Адвольная форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Працоўны стол"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Закрыць усе"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: разгарнуць"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: згарнуць"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 8ceef77..adabb31 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Фиксиране"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Свободна форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"За компютър"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Отхвърляне на всички"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"разгъване на <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"свиване на <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 14b86de..7d93d9b 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"পিন করুন"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ফ্রি-ফর্ম"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ডেস্কটপ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"সব বাতিল করুন"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> বড় করুন"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> আড়াল করুন"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index b60436c..2ac0ab1 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodan oblik"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Radna površina"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Odbacivanje svega"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširivanje oblačića <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sužavanje oblačića <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 4447c01..1c27b08 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixa"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format lliure"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escriptori"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ignora-ho tot"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"desplega <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"replega <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 711cbfa..2f8d4b0 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Připnout"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Neomezený režim"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Počítač"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Zavřít vše"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozbalit <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sbalit <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 2a5b34d..4684af2 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fastgør"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frit format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Computertilstand"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Computer"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Afvis alle"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"udvid <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skjul <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 478a7a3..155abb4 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktopmodus"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Alle schließen"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"„<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“ maximieren"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"„<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“ minimieren"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index e47b423..39e8916 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Καρφίτσωμα"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Ελεύθερη μορφή"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Υπολογιστής"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Παράβλεψη όλων"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ανάπτυξη <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"σύμπτυξη <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 04b04dd..bc73bfe 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index e0787ca..da4effb 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -22,6 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<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_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>
@@ -154,4 +155,5 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="search_gesture_feature_title" msgid="1294044108313175306">"Circle to Search"</string>
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 04b04dd..bc73bfe 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 04b04dd..bc73bfe 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index dd8de5f..25bbba2 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escritorio"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Descartar todo"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expandir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index d8bbc55..a4d7a8b 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escritorio"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Cerrar todo"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"desplegar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 114f3a1..63d4d2f 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Kinnita"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vabavorm"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Lauaarvuti režiim"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Loobu kõigist"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Toiminguriba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> laiendamine"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Toiminguriba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ahendamine"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 45fa579..3c0698c 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ainguratu"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Modu librea"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Ordenagailua"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Baztertu guztiak"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"zabaldu <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"tolestu <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index d3e3800..8d5d15e 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"پین"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"حالت رایانه"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"رد کردن همه"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ازهم باز کردن <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"جمع کردن <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 54a0c23..10ea9ee 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Kiinnitä"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vapaamuotoinen"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Tietokone"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hylkää kaikki"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"laajenna <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"tiivistä <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 591c7b7..b624b4f 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forme libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Ordinateur de bureau"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Ordinateur de 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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tout ignorer"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Développer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Réduire <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 6371f30..3f61d65 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Ordinateur"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tout fermer"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Développer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Réduire <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 0603284..d9d06a1 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Escritorio"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Pechar todo"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"despregar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 4a8e9f9..7716d4d 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"પિન કરો"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ફ્રિફોર્મ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ડેસ્કટૉપ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"તમામ છોડી દો"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> મોટો કરો"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> નાનો કરો"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 2cec388..02e23ec 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करें"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ़्रीफ़ॉर्म"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"डेस्कटॉप"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"सभी खारिज करें"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> को बड़ा करें"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> को छोटा करें"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index ed52e90..a742c01 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Prikvači"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Računalo"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Odbaci sve"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sažmite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 27db3e0..439d6ed 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Kitűzés"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Szabad forma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Asztali"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Az összes elvetése"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> kibontása"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> összecsukása"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 9a2cb2e..87681c0 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ամրացնել"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Կամայական ձև"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Համակարգիչ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Փակել բոլորը"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>. ծավալել"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>. ծալել"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 5ddfb7e..119ed6b 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Sematkan"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format bebas"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tutup semua"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"luaskan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ciutkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 3aec0ce..68f4656 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Festa"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frjálst snið"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Tölva"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hunsa allt"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"stækka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"minnka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index b9e6f62..0498c4e 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Blocca su schermo"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libera"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ignora tutte"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"espandi <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"comprimi <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 2a016fa..ad52560 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"הצמדה"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"מצב חופשי"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"במחשב"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ביטול של הכול"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"הרחבה של <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"כיווץ של <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 28b7746..4cfe9ed 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"フリーフォーム"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"デスクトップ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"すべて解除"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>を開きます"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>を閉じます"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index d84d53e..c02e9ad 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ჩამაგრება"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"თავისუფალი ფორმა"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"დესკტოპი"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ყველას დახურვა"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-ის გაფართოება"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-ის ჩაკეცვა"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 4cdbfc4..64b3e53 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Бекіту"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Еркін форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Компьютер"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Барлығын жабу"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: жаю"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: жию"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 5cf1b92..d962ba5 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ខ្ទាស់"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"មុខងារទម្រង់សេរី"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ដែសថប"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ច្រានចោលទាំងអស់"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ពង្រីក <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"បង្រួម <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 63b0006..3a05e21 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ಪಿನ್ ಮಾಡಿ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ಮುಕ್ತಸ್ವರೂಪ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ಡೆಸ್ಕ್ಟಾಪ್"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ಎಲ್ಲವನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 589cc22..6a7ba74 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"고정"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"자유 형식"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"데스크톱"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"모두 닫기"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> 펼치기"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> 접기"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index faf5675..e92b920 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Кадап коюу"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Эркин форма режими"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Компьютер"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Баарын четке кагуу"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> жайып көрсөтүү"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> жыйыштыруу"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 622db2d..f45c8ce 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ປັກໝຸດ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ຮູບແບບອິດສະຫລະ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ເດັສທັອບ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ປິດທັງໝົດ"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ຂະຫຍາຍ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ຫຍໍ້ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ລົງ"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index a95249b..c488859 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Prisegti"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Laisva forma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Stalinis kompiuteris"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,5 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Atsisakyti visų"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"išskleisti „<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sutraukti „<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“"</string>
+ <string name="search_gesture_feature_title" msgid="1294044108313175306">"Paieška apibrėžiant"</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 1892f64..23e64a7 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Piespraust"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Brīva forma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Dators"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Nerādīt nevienu"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"izvērst “<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sakļaut “<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index ce4e1b0..f0588e8 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Работна површина"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Отфрли ги сите"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"прошири <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"собери <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index c15a241..833090c 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"പിൻ ചെയ്യുക"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ഫ്രീഫോം"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ഡെസ്ക്ടോപ്പ്"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"എല്ലാം ഡിസ്മിസ് ചെയ്യുക"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> വികസിപ്പിക്കുക"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ചുരുക്കുക"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 1ff7502..100b8ed 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Бэхлэх"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Чөлөөтэй хувьсах"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Компьютер"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Бүгдийг үл хэрэгсэх"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-г дэлгэх"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-г хураах"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 215ae3f..870cf4e 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करा"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रीफॉर्म"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"डेस्कटॉप"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"सर्व डिसमिस करा"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> चा विस्तार करा"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> कोलॅप्स करा"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index a1f19a9..bed2fce 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Semat"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Bentuk bebas"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ketepikan semua"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"kembangkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"kuncupkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index eeb774b..ae0f66d 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ပင်ထိုးရန်"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"အလွတ်ပုံစံ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ဒက်စ်တော့"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"အားလုံးကို ပယ်ရန်"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ကို ပိုပြပါ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ကို လျှော့ပြပါ"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index b62b7fd..1ce18c1 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fest"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Skrivebord"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Lukk alle"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"vis <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skjul <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index a2d4d32..1921fed 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"पिन गर्नुहोस्"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रिफर्म"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"डेस्कटप"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"सबै हटाउनुहोस्"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index ee876cf..995d8d4 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Vastzetten"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vrije vorm"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Alles sluiten"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> uitvouwen"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> samenvouwen"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 3ee59b5..430058b 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ପିନ୍"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ଡେସ୍କଟପ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ସବୁ ଖାରଜ କରନ୍ତୁ"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 1e8d52e..d8d0907 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ਪਿੰਨ ਕਰੋ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ਫ੍ਰੀਫਾਰਮ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ਡੈਸਕਟਾਪ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ਸਭ ਖਾਰਜ ਕਰੋ"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 64adddf..957e5c4 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Przypnij"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Tryb dowolny"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Pulpit"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Zamknij wszystkie"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozwiń dymek: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"zwiń dymek: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 2167875..ca7cd58 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Computador"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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">"Definições de utilização de aplicações"</string>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ignorar tudo"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expandir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"reduzir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 9309810..aee1c8d 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Modo área de trabalho"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dispensar todos"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"abrir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"fechar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 19075cd..c05b85b 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixează"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formă liberă"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Închide-le pe toate"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"extinde <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"restrânge <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 76c4e1f..fca0a05 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закрепить"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Произвольная форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Мультиоконный режим"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Закрыть все"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Развернуто: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Свернуто: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 0953b38..cfbf1dd 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"අමුණන්න"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ඩෙස්ක්ටොපය"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"සියල්ල ඉවතලන්න"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> දිග හරින්න"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> හකුළන්න"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 9b682e6..b3145de 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pripnúť"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Voľný režim"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Počítač"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Zavrieť všetko"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozbaliť <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"zbaliť <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 94de1e05..d72436a 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pripni"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Prosta oblika"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Namizni računalnik"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Opusti vse"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"razširitev oblačka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"strnitev oblačka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 29214c9..30ba61d 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Gozhdo"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formë e lirë"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktopi"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hiqi të gjitha"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"zgjero <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"palos <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index d6e5d03..b83a4f2 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Слободни облик"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Рачунар"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Одбаци све"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"проширите облачић <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"скупите облачић <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index bba98c6..5ab3866 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fäst"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Dator"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<string name="recent_task_desktop" msgid="8081113562549637334">"Dator"</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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Stäng alla"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"utöka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"komprimera <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index f8d6a4f..9d13df8 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Bandika"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Muundo huru"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Kompyuta ya mezani"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ondoa vyote"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"panua <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"kunja <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 73c6c37..8ac0ff8 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"பின் செய்தல்"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"குறிப்பிட்ட வடிவமில்லாத பயன்முறை"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"டெஸ்க்டாப்"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"அனைத்தையும் மூடும்"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ஐ விரிவாக்கும்"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 91ef846..4ae7a58 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"పిన్ చేయండి"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"సంప్రదాయేతర"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"డెస్క్టాప్"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"అన్నింటినీ విస్మరించండి"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ను విస్తరించండి"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ను కుదించండి"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 2218e6d0..210e996 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ปักหมุด"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"รูปแบบอิสระ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"เดสก์ท็อป"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ปิดทั้งหมด"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ขยาย <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ยุบ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index fac6a52..13d89a2 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"I-pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"I-dismiss lahat"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"i-expand ang <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"i-collapse ang <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index d44d710..1cb1fa7 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Sabitle"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Serbest çalışma"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Masaüstü"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tümünü kapat"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"genişlet: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"daralt: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 320c2ea..84ddd81 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закріпити"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Довільна форма"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Робочий стіл"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Закрити все"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"розгорнути \"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>\""</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"згорнути \"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>\""</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index c71625a..43e97c2 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"پن کریں"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"فری فارم"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"ڈیسک ٹاپ"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"سبھی کو برخاست کریں"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> کو پھیلائیں"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> کو سکیڑیں"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index e379453..090753f 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Qadash"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Erkin shakl"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Desktop"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <skip />
<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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hammasini yopish"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ni yoyish"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ni yigʻish"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index ddefb9e..b0d31cb 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ghim"</string>
<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>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Đóng tất cả"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"mở rộng <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"thu gọn <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 8540cd9..73121b4 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由窗口"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"桌面"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"全部关闭"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"展开“<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收起“<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 3d16e8d..b934dfc 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"桌面"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"全部關閉"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"打開<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收埋<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index bf03812..1cba819 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"桌面"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"全部關閉"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"展開「<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>」"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收合「<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>」"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index d0d0bb6..ee6b3b7 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -22,6 +22,8 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Phina"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"I-Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Ideskithophu"</string>
+ <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
+ <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>
@@ -154,4 +156,6 @@
<string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Chitha konke"</string>
<string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"nweba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
<string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"goqa <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <!-- no translation found for search_gesture_feature_title (1294044108313175306) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index e8cb5d5..db5ff19 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -33,12 +33,11 @@
<string name="taskbar_model_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarModelCallbacksFactory</string>
<string name="taskbar_view_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarViewCallbacksFactory</string>
<string name="launcher_restore_event_logger_class" translatable="false">com.android.quickstep.LauncherRestoreEventLoggerImpl</string>
- <string name="plugin_manager_wrapper_class" translatable="false">com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl</string>
<string name="taskbar_edu_tooltip_controller_class" translatable="false">com.android.launcher3.taskbar.TaskbarEduTooltipController</string>
<string name="contextual_edu_manager_class" translatable="false">com.android.quickstep.contextualeducation.SystemContextualEduStatsManager</string>
<string name="nav_handle_long_press_handler_class" translatable="false"></string>
- <string name="assist_utils_class" translatable="false"></string>
- <string name="assist_state_manager_class" translatable="false"></string>
+ <string name="contextual_search_invoker_class" translatable="false"></string>
+ <string name="contextual_search_state_manager_class" translatable="false"></string>
<string name="api_wrapper_class" translatable="false">com.android.launcher3.uioverrides.SystemApiWrapper</string>
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 8957e0d..9836172 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -424,6 +424,7 @@
<!--- Taskbar Pinning -->
<dimen name="taskbar_pinning_popup_menu_width">300dp</dimen>
<dimen name="taskbar_pinning_popup_menu_vertical_margin">16dp</dimen>
+ <dimen name="taskbar_pinning_popup_menu_min_padding_from_screen_edge">16dp</dimen>
<!--- Floating Ime Inset height-->
<dimen name="floating_ime_inset_height">60dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 008766b..026e25c 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -28,6 +28,8 @@
<string name="recent_task_option_freeform">Freeform</string>
<!-- Title and content description for an option to enter desktop windowing mode for a given app -->
<string name="recent_task_option_desktop">Desktop</string>
+ <!-- Title and content description for an option to move app to external display. -->
+ <string name="recent_task_option_external_display">Move to external display</string>
<!-- Title and content description for Desktop tile in Recents screen that contains apps opened inside desktop windowing mode [CHAR LIMIT=NONE] -->
<string name="recent_task_desktop">Desktop</string>
@@ -360,4 +362,8 @@
<string name="bubble_bar_accessibility_announce_expand">expand <xliff:g id="bubble_description" example="some title from Messages">%1$s</xliff:g></string>
<!-- Accessibility announcement when the bubble bar collapses. [CHAR LIMIT=NONE]-->
<string name="bubble_bar_accessibility_announce_collapse">collapse <xliff:g id="bubble_description" example="some title from Messages">%1$s</xliff:g></string>
+
+ <!-- Name of Google's new feature to circle to search anything on your phone screen, without
+ switching apps. [CHAR_LIMIT=60] -->
+ <string name="search_gesture_feature_title">Circle to Search</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
new file mode 100644
index 0000000..dd2ff2d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 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.desktop
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.Context
+import android.os.IBinder
+import android.view.SurfaceControl.Transaction
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.IRemoteTransitionFinishedCallback
+import android.window.RemoteTransitionStub
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import androidx.core.animation.addListener
+import com.android.app.animation.Interpolators
+import com.android.quickstep.RemoteRunnable
+import java.util.concurrent.Executor
+
+/**
+ * [android.window.RemoteTransition] for Desktop app launches.
+ *
+ * This transition supports minimize-changes, i.e. in a launch-transition, if a window is moved back
+ * ([android.view.WindowManager.TRANSIT_TO_BACK]) this transition will apply a minimize animation to
+ * that window.
+ */
+class DesktopAppLaunchTransition(private val context: Context, private val mainExecutor: Executor) :
+ RemoteTransitionStub() {
+
+ override fun startAnimation(
+ token: IBinder,
+ info: TransitionInfo,
+ t: Transaction,
+ transitionFinishedCallback: IRemoteTransitionFinishedCallback,
+ ) {
+ val safeTransitionFinishedCallback = RemoteRunnable {
+ transitionFinishedCallback.onTransitionFinished(/* wct= */ null, /* sct= */ null)
+ }
+ mainExecutor.execute {
+ runAnimators(info, safeTransitionFinishedCallback)
+ t.apply()
+ }
+ }
+
+ private fun runAnimators(info: TransitionInfo, finishedCallback: RemoteRunnable) {
+ val animators = mutableListOf<Animator>()
+ val animatorFinishedCallback: (Animator) -> Unit = { animator ->
+ animators -= animator
+ if (animators.isEmpty()) finishedCallback.run()
+ }
+ animators += createAnimators(info, animatorFinishedCallback)
+ animators.forEach { it.start() }
+ }
+
+ private fun createAnimators(
+ info: TransitionInfo,
+ finishCallback: (Animator) -> Unit,
+ ): List<Animator> {
+ val transaction = Transaction()
+ val launchAnimator =
+ createLaunchAnimator(getLaunchChange(info), transaction, finishCallback)
+ val minimizeChange = getMinimizeChange(info) ?: return listOf(launchAnimator)
+ val minimizeAnimator = createMinimizeAnimator(minimizeChange, transaction, finishCallback)
+ return listOf(launchAnimator, minimizeAnimator)
+ }
+
+ private fun getLaunchChange(info: TransitionInfo): Change =
+ requireNotNull(info.changes.firstOrNull { change -> change.mode in LAUNCH_CHANGE_MODES }) {
+ "expected an app launch Change"
+ }
+
+ private fun getMinimizeChange(info: TransitionInfo): Change? =
+ info.changes.firstOrNull { change -> change.mode == TRANSIT_TO_BACK }
+
+ private fun createLaunchAnimator(
+ change: Change,
+ transaction: Transaction,
+ onAnimFinish: (Animator) -> Unit,
+ ): Animator {
+ val boundsAnimator =
+ WindowAnimator.createBoundsAnimator(
+ context,
+ launchBoundsAnimationDef,
+ change,
+ transaction,
+ )
+ val alphaAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = LAUNCH_ANIM_ALPHA_DURATION_MS
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { animation ->
+ transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+ }
+ }
+ return AnimatorSet().apply {
+ playTogether(boundsAnimator, alphaAnimator)
+ addListener(onEnd = { animation -> onAnimFinish(animation) })
+ }
+ }
+
+ private fun createMinimizeAnimator(
+ change: Change,
+ transaction: Transaction,
+ onAnimFinish: (Animator) -> Unit,
+ ): Animator {
+ val boundsAnimator =
+ WindowAnimator.createBoundsAnimator(
+ context,
+ minimizeBoundsAnimationDef,
+ change,
+ transaction,
+ )
+ val alphaAnimator =
+ ValueAnimator.ofFloat(1f, 0f).apply {
+ duration = MINIMIZE_ANIM_ALPHA_DURATION_MS
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { animation ->
+ transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+ }
+ }
+ return AnimatorSet().apply {
+ playTogether(boundsAnimator, alphaAnimator)
+ addListener(onEnd = { animation -> onAnimFinish(animation) })
+ }
+ }
+
+ companion object {
+ private val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
+
+ private const val LAUNCH_ANIM_ALPHA_DURATION_MS = 100L
+ private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
+
+ private val launchBoundsAnimationDef =
+ WindowAnimator.BoundsAnimationParams(
+ durationMs = 300,
+ startOffsetYDp = 12f,
+ startScale = 0.97f,
+ interpolator = Interpolators.STANDARD_DECELERATE,
+ )
+
+ private val minimizeBoundsAnimationDef =
+ WindowAnimator.BoundsAnimationParams(
+ durationMs = 200,
+ endOffsetYDp = 12f,
+ endScale = 0.97f,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
+ )
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 8b3a032..ac1ffa6 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -66,6 +66,11 @@
systemUiProxy.moveToDesktop(taskId, transitionSource)
}
+ /** Move task to external display from recents view */
+ fun moveToExternalDisplay(taskId: Int) {
+ systemUiProxy.moveToExternalDisplay(taskId)
+ }
+
private class RemoteDesktopLaunchTransitionRunner(
private val desktopTaskView: DesktopTaskView,
private val animated: Boolean,
diff --git a/quickstep/src/com/android/launcher3/desktop/WindowAnimator.kt b/quickstep/src/com/android/launcher3/desktop/WindowAnimator.kt
new file mode 100644
index 0000000..1a99a36
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/desktop/WindowAnimator.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 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.desktop
+
+import android.animation.RectEvaluator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Rect
+import android.util.TypedValue
+import android.view.SurfaceControl
+import android.view.animation.Interpolator
+import android.window.TransitionInfo
+
+/** Creates animations that can be applied to windows/surfaces. */
+object WindowAnimator {
+
+ /** Parameters defining a window bounds animation. */
+ data class BoundsAnimationParams(
+ val durationMs: Long,
+ val startOffsetYDp: Float = 0f,
+ val endOffsetYDp: Float = 0f,
+ val startScale: Float = 1f,
+ val endScale: Float = 1f,
+ val interpolator: Interpolator,
+ )
+
+ /**
+ * Creates an animator to reposition and scale the bounds of the leash of the given change.
+ *
+ * @param boundsAnimDef the parameters for the animation itself (duration, scale, position)
+ * @param change the change to which the animation should be applied
+ * @param transaction the transaction to apply the animation to
+ */
+ fun createBoundsAnimator(
+ context: Context,
+ boundsAnimDef: BoundsAnimationParams,
+ change: TransitionInfo.Change,
+ transaction: SurfaceControl.Transaction,
+ ): ValueAnimator {
+ val startBounds =
+ createBounds(
+ context,
+ change.startAbsBounds,
+ boundsAnimDef.startScale,
+ boundsAnimDef.startOffsetYDp,
+ )
+ val leash = change.leash
+ val endBounds =
+ createBounds(
+ context,
+ change.startAbsBounds,
+ boundsAnimDef.endScale,
+ boundsAnimDef.endOffsetYDp,
+ )
+ return ValueAnimator.ofObject(RectEvaluator(), startBounds, endBounds).apply {
+ duration = boundsAnimDef.durationMs
+ interpolator = boundsAnimDef.interpolator
+ addUpdateListener { animation ->
+ val animBounds = animation.animatedValue as Rect
+ val animScale = 1 - (1 - boundsAnimDef.endScale) * animation.animatedFraction
+ transaction
+ .setPosition(leash, animBounds.left.toFloat(), animBounds.top.toFloat())
+ .setScale(leash, animScale, animScale)
+ .apply()
+ }
+ }
+ }
+
+ private fun createBounds(context: Context, origBounds: Rect, scale: Float, offsetYDp: Float) =
+ Rect(origBounds).apply {
+ check(scale in 0.0..1.0)
+ // Scale the bounds down with an anchor in the center
+ inset(
+ (origBounds.width().toFloat() * (1 - scale) / 2).toInt(),
+ (origBounds.height().toFloat() * (1 - scale) / 2).toInt(),
+ )
+ val offsetYPx =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ offsetYDp,
+ context.resources.displayMetrics,
+ )
+ .toInt()
+ offset(/* dx= */ 0, offsetYPx)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 4014f06..29e1f4e 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -77,6 +77,7 @@
import com.android.launcher3.util.PersistedItemArray;
import com.android.quickstep.logging.SettingsChangeLogger;
import com.android.quickstep.logging.StatsLogCompatManager;
+import com.android.quickstep.util.ContextualSearchStateManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList;
@@ -209,6 +210,8 @@
@Override
public void workspaceLoadComplete() {
super.workspaceLoadComplete();
+ // Initialize ContextualSearchStateManager.
+ ContextualSearchStateManager.INSTANCE.get(mContext);
recreatePredictors();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 1c8a094..a80c11c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.animation.Animator;
@@ -31,6 +32,7 @@
import com.android.internal.jank.Cuj;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
import com.android.launcher3.views.BaseDragLayer;
@@ -41,6 +43,7 @@
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.List;
@@ -181,7 +184,7 @@
Runnable onFinishCallback = () -> InteractionJankMonitorWrapper.end(
Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH);
TaskbarActivityContext context = mControllers.taskbarActivityContext;
- RemoteTransition remoteTransition = new RemoteTransition(new SlideInRemoteTransition(
+ final RemoteTransition slideInTransition = new RemoteTransition(new SlideInRemoteTransition(
Utilities.isRtl(mControllers.taskbarActivityContext.getResources()),
context.getDeviceProfile().overviewPageSpacing,
QuickStepContract.getWindowCornerRadius(context),
@@ -195,7 +198,7 @@
SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
.showDesktopApps(
mKeyboardQuickSwitchView.getDisplay().getDisplayId(),
- remoteTransition));
+ slideInTransition));
return -1;
}
// Even with a valid index, this can be null if the user tries to quick switch before the
@@ -208,6 +211,13 @@
// Ignore attempts to run the selected task if it is already running.
return -1;
}
+ RemoteTransition remoteTransition = slideInTransition;
+ if (mOnDesktop && task.task1.isMinimized
+ && Flags.enableDesktopAppLaunchAlttabTransitions()) {
+ // This app is being unminimized - use our own transition runner.
+ remoteTransition = new RemoteTransition(
+ new DesktopAppLaunchTransition(context, MAIN_EXECUTOR));
+ }
mControllers.taskbarActivityContext.handleGroupTaskLaunch(
task,
remoteTransition,
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 09dbeb6..0b850bd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -36,6 +36,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
@@ -68,14 +69,17 @@
public static final int ALL_APPS_PAGE_PROGRESS_INDEX = 1;
public static final int WIDGETS_PAGE_PROGRESS_INDEX = 2;
public static final int SYSUI_SURFACE_PROGRESS_INDEX = 3;
+ public static final int LAUNCHER_PAUSE_PROGRESS_INDEX = 4;
- public static final int DISPLAY_PROGRESS_COUNT = 4;
+ public static final int DISPLAY_PROGRESS_COUNT = 5;
private final AnimatedFloat mTaskbarInAppDisplayProgress = new AnimatedFloat(
this::onInAppDisplayProgressChanged);
private final MultiPropertyFactory<AnimatedFloat> mTaskbarInAppDisplayProgressMultiProp =
new MultiPropertyFactory<>(mTaskbarInAppDisplayProgress,
AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
+ private final AnimatedFloat mLauncherPauseProgress = new AnimatedFloat(
+ this::launcherPauseProgressUpdate);
private final QuickstepLauncher mLauncher;
private final HomeVisibilityState mHomeState;
@@ -150,12 +154,6 @@
}
}
- @Override
- protected boolean isTaskbarTouchable() {
- return !(mTaskbarLauncherStateController.isAnimatingToLauncher()
- && mTaskbarLauncherStateController.isTaskbarAlignedWithHotseat());
- }
-
public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
mTaskbarLauncherStateController.setShouldDelayLauncherStateAnim(
shouldDelayLauncherStateAnim);
@@ -191,6 +189,33 @@
}
/**
+ * Called when Launcher Activity is paused/resumed.
+ * <p>
+ * To avoid UI clash between taskbar & bottom sheet, shift nav buttons down on launcher
+ * pause/resume at home.
+ * @param paused if launcher is currently paused.
+ */
+ public void onLauncherPausedOrResumed(boolean paused) {
+ if (!FeatureFlags.enableHomeTransitionListener()) {
+ onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
+ return;
+ }
+
+ // Animate navbar iff pause/resume from home, NOT to/from app (avoid overriding existing
+ // animations).
+ boolean launcherPauseOrResumeFromHome = mHomeState.isHomeVisible() && mControllers
+ .taskbarAutohideSuspendController.isSuspendedForTransientTaskbarInLauncher();
+ if (launcherPauseOrResumeFromHome) {
+ mLauncherPauseProgress.animateToValue(paused ? 1.0f : 0.0f).start();
+ }
+ }
+
+ private void launcherPauseProgressUpdate() {
+ onTaskbarInAppDisplayProgressUpdate(
+ mLauncherPauseProgress.value, LAUNCHER_PAUSE_PROGRESS_INDEX);
+ }
+
+ /**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
@Override
@@ -364,18 +389,20 @@
}
if (mControllers.uiController.isIconAlignedWithHotseat()
&& !mTaskbarLauncherStateController.isAnimatingToLauncher()) {
- // Only animate the nav buttons while home and not animating home, otherwise let
+ // Only animate nav button position while home and not animating home, otherwise let
// the TaskbarViewController handle it.
mControllers.navbarButtonsViewController
- .getTaskbarNavButtonTranslationYForInAppDisplay()
+ .getNavButtonTranslationYForInAppDisplay()
.updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY()
* mTaskbarInAppDisplayProgress.value);
- mControllers.navbarButtonsViewController
- .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
+ if (!mLauncher.isPaused()) {
+ mControllers.navbarButtonsViewController
+ .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
+ }
}
}
- /** Returns true iff any in-app display progress > 0. */
+ @Override
public boolean shouldUseInAppLayout() {
return mTaskbarInAppDisplayProgress.value > 0;
}
@@ -420,6 +447,16 @@
}
@Override
+ public boolean isHotseatVisibleForTaskBarAlignment() {
+ return mTaskbarLauncherStateController.isHotseatVisibleForTaskbarAlignment();
+ }
+
+ @Override
+ public boolean isAnimatingToLauncher() {
+ return mTaskbarLauncherStateController.isAnimatingToLauncher();
+ }
+
+ @Override
protected boolean isInOverviewUi() {
return mTaskbarLauncherStateController.isInOverviewUi();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 7d8e93c..cfcbd2f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -183,7 +183,7 @@
private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
this::updateNavButtonTranslationY);
- private final AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay = new AnimatedFloat(
+ private final AnimatedFloat mNavButtonTranslationYForInAppDisplay = new AnimatedFloat(
this::updateNavButtonTranslationY);
private final AnimatedFloat mTaskbarNavButtonTranslationYForIme = new AnimatedFloat(
this::updateNavButtonTranslationY);
@@ -704,8 +704,8 @@
}
/** Use to set the translationY for the all nav+contextual buttons when in Launcher */
- public AnimatedFloat getTaskbarNavButtonTranslationYForInAppDisplay() {
- return mTaskbarNavButtonTranslationYForInAppDisplay;
+ public AnimatedFloat getNavButtonTranslationYForInAppDisplay() {
+ return mNavButtonTranslationYForInAppDisplay;
}
/** Use to set the dark intensity for the all nav+contextual buttons */
@@ -751,21 +751,23 @@
if (mContext.isPhoneButtonNavMode()) {
return;
}
- final float normalTranslationY = mTaskbarNavButtonTranslationY.value;
- final float imeAdjustmentTranslationY = mTaskbarNavButtonTranslationYForIme.value;
- TaskbarUIController uiController = mControllers.uiController;
- final float inAppDisplayAdjustmentTranslationY =
- (uiController instanceof LauncherTaskbarUIController
- && ((LauncherTaskbarUIController) uiController).shouldUseInAppLayout())
- ? mTaskbarNavButtonTranslationYForInAppDisplay.value : 0;
-
- mLastSetNavButtonTranslationY = normalTranslationY
- + imeAdjustmentTranslationY
- + inAppDisplayAdjustmentTranslationY;
+ mLastSetNavButtonTranslationY = calculateNavButtonTranslationY();
mNavButtonsView.setTranslationY(mLastSetNavButtonTranslationY);
}
/**
+ * Calculates the translationY of the nav buttons based on the current device state.
+ */
+ private float calculateNavButtonTranslationY() {
+ float translationY =
+ mTaskbarNavButtonTranslationY.value + mTaskbarNavButtonTranslationYForIme.value;
+ if (mControllers.uiController.shouldUseInAppLayout()) {
+ translationY += mNavButtonTranslationYForInAppDisplay.value;
+ }
+ return translationY;
+ }
+
+ /**
* Sets Taskbar 3-button mode icon colors based on the
* {@link #mTaskbarNavButtonDarkIntensity} value piped in from Framework. For certain cases
* in large screen taskbar where there may be opaque surfaces, the selected SystemUI button
@@ -1162,7 +1164,7 @@
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationY="
+ mTaskbarNavButtonTranslationY.value);
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForInAppDisplay="
- + mTaskbarNavButtonTranslationYForInAppDisplay.value);
+ + mNavButtonTranslationYForInAppDisplay.value);
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForIme="
+ mTaskbarNavButtonTranslationYForIme.value);
pw.println(prefix + "\t\tmTaskbarNavButtonDarkIntensity="
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 1b9614a..12be99d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -952,6 +952,12 @@
* Hides the taskbar icons and background when the notification shade is expanded.
*/
private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
+ // Close all floating views within the Taskbar window to make sure nothing is shown over
+ // the notification shade.
+ if (isExpanded) {
+ AbstractFloatingView.closeAllOpenViewsExcept(this, TYPE_TASKBAR_OVERLAY_PROXY);
+ }
+
float alpha = isExpanded ? 0 : 1;
AnimatorSet anim = new AnimatorSet();
anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().get(
@@ -1234,7 +1240,8 @@
}
} else if (tag instanceof TaskItemInfo info) {
UI_HELPER_EXECUTOR.execute(() ->
- SystemUiProxy.INSTANCE.get(this).showDesktopApp(info.getTaskId()));
+ SystemUiProxy.INSTANCE.get(this).showDesktopApp(
+ info.getTaskId(), /* remoteTransition= */ null));
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
/* stash= */ true);
} else if (tag instanceof WorkspaceItemInfo) {
@@ -1325,7 +1332,8 @@
GroupTask task,
@Nullable RemoteTransition remoteTransition,
boolean onDesktop) {
- handleGroupTaskLaunch(task, remoteTransition, onDesktop, null, null);
+ handleGroupTaskLaunch(task, remoteTransition, onDesktop,
+ /* onStartCallback= */ null, /* onFinishCallback= */ null);
}
/**
@@ -1349,17 +1357,24 @@
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(this).showDesktopApps(getDisplay().getDisplayId(),
remoteTransition));
- } else if (onDesktop) {
+ return;
+ }
+ if (onDesktop) {
+ boolean useRemoteTransition = task.task1.isMinimized
+ && com.android.window.flags.Flags.enableDesktopAppLaunchAlttabTransitions();
UI_HELPER_EXECUTOR.execute(() -> {
if (onStartCallback != null) {
onStartCallback.run();
}
- SystemUiProxy.INSTANCE.get(this).showDesktopApp(task.task1.key.id);
+ SystemUiProxy.INSTANCE.get(this).showDesktopApp(
+ task.task1.key.id, useRemoteTransition ? remoteTransition : null);
if (onFinishCallback != null) {
onFinishCallback.run();
}
});
- } else if (task.task2 == null) {
+ return;
+ }
+ if (task.task2 == null) {
UI_HELPER_EXECUTOR.execute(() -> {
ActivityOptions activityOptions =
makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED).options;
@@ -1368,9 +1383,9 @@
ActivityManagerWrapper.getInstance().startActivityFromRecents(
task.task1.key, activityOptions);
});
- } else {
- mControllers.uiController.launchSplitTasks(task, remoteTransition);
+ return;
}
+ mControllers.uiController.launchSplitTasks(task, remoteTransition);
}
/**
@@ -1547,6 +1562,7 @@
/**
* Called when we want to unstash taskbar when user performs swipes up gesture.
+ *
* @param delayTaskbarBackground whether we will delay the taskbar background animation
*/
public void onSwipeToUnstashTaskbar(boolean delayTaskbarBackground) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index b5a3314..69bc6bd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -31,21 +31,21 @@
import android.widget.Switch
import androidx.core.view.postDelayed
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.launcher3.Flags
import com.android.launcher3.R
import com.android.launcher3.popup.ArrowPopup
import com.android.launcher3.popup.RoundedArrowDrawable
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
+import kotlin.math.max
+import kotlin.math.min
/** Popup view with arrow for taskbar pinning */
class TaskbarDividerPopupView<T : TaskbarActivityContext>
@JvmOverloads
-constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0,
-) : ArrowPopup<T>(context, attrs, defStyleAttr) {
+constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
+ ArrowPopup<T>(context, attrs, defStyleAttr) {
companion object {
private const val TAG = "TaskbarDividerPopupView"
private const val DIVIDER_POPUP_CLOSING_DELAY = 333L
@@ -55,24 +55,28 @@
fun createAndPopulate(
view: View,
taskbarActivityContext: TaskbarActivityContext,
+ horizontalPosition: Float,
): TaskbarDividerPopupView<*> {
val taskMenuViewWithArrow =
taskbarActivityContext.layoutInflater.inflate(
R.layout.taskbar_divider_popup_menu,
taskbarActivityContext.dragLayer,
- false
+ false,
) as TaskbarDividerPopupView<*>
- return taskMenuViewWithArrow.populateForView(view)
+ return taskMenuViewWithArrow.populateForView(view, horizontalPosition)
}
}
private lateinit var dividerView: View
+ private var horizontalPosition = 0.0f
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height)
private val arrowPointRadius = resources.getDimension(R.dimen.popup_arrow_corner_radius)
+ private val minPaddingFromScreenEdge =
+ resources.getDimension(R.dimen.taskbar_pinning_popup_menu_min_padding_from_screen_edge)
private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
private var didPreferenceChange = false
@@ -128,7 +132,15 @@
/** Orient object as usual and then center object horizontally. */
override fun orientAboutObject() {
super.orientAboutObject()
- x = mTempRect.centerX() - measuredWidth / 2f
+ x =
+ if (Flags.showTaskbarPinningPopupFromAnywhere()) {
+ min(
+ max(minPaddingFromScreenEdge, horizontalPosition - measuredWidth / 2f),
+ popupContainer.getWidth() - measuredWidth - minPaddingFromScreenEdge,
+ )
+ } else {
+ mTempRect.centerX() - measuredWidth / 2f
+ }
}
override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
@@ -142,8 +154,9 @@
return false
}
- private fun populateForView(view: View): TaskbarDividerPopupView<*> {
+ private fun populateForView(view: View, horizontalPosition: Float): TaskbarDividerPopupView<*> {
dividerView = view
+ this@TaskbarDividerPopupView.horizontalPosition = horizontalPosition
tryUpdateBackground()
return this
}
@@ -169,15 +182,21 @@
override fun addArrow() {
super.addArrow()
- val location = IntArray(2)
- popupContainer.getLocationInDragLayer(dividerView, location)
- val dividerViewX = location[0].toFloat()
- // Change arrow location to the middle of popup.
- mArrow.x = (dividerViewX + dividerView.width / 2) - (mArrowWidth / 2)
+ if (Flags.showTaskbarPinningPopupFromAnywhere()) {
+ mArrow.x = horizontalPosition - mArrowWidth / 2
+ } else {
+ val location = IntArray(2)
+ popupContainer.getLocationInDragLayer(dividerView, location)
+ val dividerViewX = location[0].toFloat()
+ // Change arrow location to the middle of popup.
+ mArrow.x = (dividerViewX + dividerView.width / 2) - (mArrowWidth / 2)
+ }
}
override fun updateArrowColor() {
- if (!Gravity.isVertical(mGravity)) {
+ if (Flags.showTaskbarPinningPopupFromAnywhere()) {
+ super.updateArrowColor()
+ } else if (!Gravity.isVertical(mGravity)) {
mArrow.background =
RoundedArrowDrawable(
arrowWidth,
@@ -227,13 +246,13 @@
ObjectAnimator.ofFloat(
this,
TRANSLATION_Y,
- *floatArrayOf(this.translationY, this.translationY + translateYValue)
+ *floatArrayOf(this.translationY, this.translationY + translateYValue),
)
val arrowTranslateY =
ObjectAnimator.ofFloat(
mArrow,
TRANSLATION_Y,
- *floatArrayOf(mArrow.translationY, mArrow.translationY + translateYValue)
+ *floatArrayOf(mArrow.translationY, mArrow.translationY + translateYValue),
)
val animatorSet = AnimatorSet()
animatorSet.playTogether(alpha, arrowAlpha, translateY, arrowTranslateY)
@@ -243,7 +262,7 @@
private fun getAnimatorOfFloat(
view: View,
property: Property<View, Float>,
- vararg values: Float
+ vararg values: Float,
): Animator {
val animator: Animator = ObjectAnimator.ofFloat(view, property, *values)
animator.setDuration(DIVIDER_POPUP_CLOSING_ANIMATION_DURATION)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 06376d3..ade8f8c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -49,6 +49,7 @@
import com.android.launcher3.util.ResourceBasedOverride
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.BaseDragLayer
+import com.android.quickstep.util.ContextualSearchInvoker
import com.android.quickstep.util.LottieAnimationColorUtils
import java.io.PrintWriter
@@ -80,7 +81,11 @@
ResourceBasedOverride, LoggableTaskbarController {
protected val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
- open val shouldShowSearchEdu = false
+ open val shouldShowSearchEdu: Boolean
+ get() =
+ ContextualSearchInvoker.newInstance(activityContext)
+ .runContextualSearchInvocationChecksAndLogFailures()
+
private val isTooltipEnabled: Boolean
get() {
return !Utilities.isRunningInTestHarness() &&
@@ -351,19 +356,19 @@
overlayContext.layoutInflater.inflate(
R.layout.taskbar_edu_tooltip,
overlayContext.dragLayer,
- false
+ false,
) as TaskbarEduTooltip
controllers.taskbarAutohideSuspendController.updateFlag(
FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
- true
+ true,
)
tooltip.onCloseCallback = {
this.tooltip = null
controllers.taskbarAutohideSuspendController.updateFlag(
FLAG_AUTOHIDE_SUSPEND_EDU_OPEN,
- false
+ false,
)
controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
}
@@ -378,7 +383,7 @@
override fun performAccessibilityAction(
host: View,
action: Int,
- args: Bundle?
+ args: Bundle?,
): Boolean {
if (action == R.id.close) {
hide()
@@ -396,13 +401,13 @@
override fun onInitializeAccessibilityNodeInfo(
host: View,
- info: AccessibilityNodeInfo
+ info: AccessibilityNodeInfo,
) {
super.onInitializeAccessibilityNodeInfo(host, info)
info.addAction(
AccessibilityNodeInfo.AccessibilityAction(
R.id.close,
- host.context?.getText(R.string.taskbar_edu_close)
+ host.context?.getText(R.string.taskbar_edu_close),
)
)
}
@@ -421,7 +426,7 @@
return ResourceBasedOverride.Overrides.getObject(
TaskbarEduTooltipController::class.java,
context,
- R.string.taskbar_edu_tooltip_controller_class
+ R.string.taskbar_edu_tooltip_controller_class,
)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 685c109..55722a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -47,6 +47,7 @@
import com.android.internal.policy.GestureNavigationSettingsObserver
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
+import com.android.launcher3.Utilities
import com.android.launcher3.anim.AlphaUpdateListener
import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION
import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate
@@ -82,7 +83,7 @@
context.mainThreadHandler,
Executors.UI_HELPER_EXECUTOR.handler,
context,
- this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged
+ this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged,
)
private val debugTouchableRegion = DebugTouchableRegion()
@@ -120,7 +121,7 @@
if (enableTaskbarNoRecreate() && controllers.sharedState != null) {
getProvidedInsets(
controllers.sharedState!!.insetsFrameProviders,
- insetsRoundedCornerFlag
+ insetsRoundedCornerFlag,
)
} else {
getProvidedInsets(insetsRoundedCornerFlag)
@@ -133,9 +134,6 @@
}
val bubbleControllers = controllers.bubbleControllers.getOrNull()
- val taskbarTouchableHeight = taskbarStashController.touchableHeight
- val bubblesTouchableHeight =
- bubbleControllers?.bubbleStashController?.getTouchableHeight() ?: 0
// reset touch bounds
defaultTouchableRegion.setEmpty()
if (bubbleControllers != null) {
@@ -147,16 +145,41 @@
defaultTouchableRegion.addBoundsToRegion(bubbleBarViewController.bubbleBarBounds)
}
}
+ val uiController = controllers.uiController
if (
- taskbarStashController.isInApp ||
- taskbarStashController.isInOverview ||
+ !uiController.isHotseatVisibleForTaskBarAlignment ||
DisplayController.showLockedTaskbarOnHome(context)
) {
- // only add the taskbar touch region if not on home
+ // adding the taskbar touch region
+ var left = 0
+ var right = context.deviceProfile.widthPx
+ val touchableHeight: Int
+ if (uiController.isAnimatingToLauncher) {
+ val dp = controllers.taskbarActivityContext.deviceProfile
+ touchableHeight = windowLayoutParams.height
+ if (dp.isQsbInline) {
+ // if Qsb is inline need to exclude search icon from touch region
+ val isRtl = Utilities.isRtl(context.resources)
+ val navBarOffset =
+ bubbleControllers?.bubbleBarViewController?.let {
+ val isBubblesOnLeft = it.bubbleBarLocation.isOnLeft(isRtl)
+ dp.getHotseatTranslationXForNavBar(context, isBubblesOnLeft)
+ } ?: 0
+ val hotseatPadding: Rect = dp.getHotseatLayoutPadding(context)
+ val borderSpacing: Int = dp.hotseatBorderSpace
+ if (isRtl) {
+ right = dp.widthPx - hotseatPadding.right + borderSpacing + navBarOffset
+ } else {
+ left = hotseatPadding.left - borderSpacing + navBarOffset
+ }
+ }
+ } else {
+ // if not animating to launcher use the taskbar touchanle height
+ touchableHeight = taskbarStashController.touchableHeight
+ }
val bottom = windowLayoutParams.height
- val top = bottom - taskbarTouchableHeight
- val right = context.deviceProfile.widthPx
- defaultTouchableRegion.addBoundsToRegion(Rect(/* left= */ 0, top, right, bottom))
+ val top = bottom - touchableHeight
+ defaultTouchableRegion.addBoundsToRegion(Rect(left, top, right, bottom))
}
// Pre-calculate insets for different providers across different rotations for this gravity
@@ -181,7 +204,7 @@
*/
private fun getProvidedInsets(
providedInsets: Array<InsetsFrameProvider>,
- insetsRoundedCornerFlag: Int
+ insetsRoundedCornerFlag: Int,
): Array<InsetsFrameProvider> {
val navBarsFlag =
(if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
@@ -207,14 +230,14 @@
InsetsFrameProvider(insetsOwner, 0, navigationBars())
.setFlags(
navBarsFlag,
- FLAG_SUPPRESS_SCRIM or FLAG_ANIMATE_RESIZING or FLAG_INSETS_ROUNDED_CORNER
+ FLAG_SUPPRESS_SCRIM or FLAG_ANIMATE_RESIZING or FLAG_INSETS_ROUNDED_CORNER,
),
InsetsFrameProvider(insetsOwner, 0, tappableElement()),
InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
.setSource(SOURCE_DISPLAY),
InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
- .setSource(SOURCE_DISPLAY)
+ .setSource(SOURCE_DISPLAY),
)
}
@@ -232,7 +255,7 @@
val gestureHeight =
ResourceUtils.getNavbarSize(
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- context.resources
+ context.resources,
)
val isPinnedTaskbar =
context.deviceProfile.isTaskbarPresent &&
@@ -272,8 +295,8 @@
// override below (insetsSizeOverrides must have the same length and
// types after the window is added according to
// WindowManagerService#relayoutWindow)
- provider.insetsSize
- )
+ provider.insetsSize,
+ ),
)
// Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
val visInsetsSizeForTappableElement =
@@ -284,7 +307,7 @@
InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
InsetsFrameProvider.InsetsSizeOverride(
TYPE_VOICE_INTERACTION,
- visInsetsSizeForTappableElement
+ visInsetsSizeForTappableElement,
),
)
if (
@@ -368,10 +391,6 @@
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
debugTouchableRegion.lastSetTouchableReason = "Stashed over IME"
- } else if (!controllers.uiController.isTaskbarTouchable) {
- // Let touches pass through us.
- insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
- debugTouchableRegion.lastSetTouchableReason = "Taskbar is not touchable"
} else if (controllers.taskbarDragController.isSystemDragInProgress) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
@@ -427,7 +446,7 @@
// Always have nav buttons be touchable
controllers.navbarButtonsViewController.addVisibleButtonsRegion(
context.dragLayer,
- insetsInfo.touchableRegion
+ insetsInfo.touchableRegion,
)
debugTouchableRegion.lastSetTouchableBounds.set(insetsInfo.touchableRegion.bounds)
context.excludeFromMagnificationRegion(insetsIsTouchableRegion)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 707d4b3..4bc7d3c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_ALIGNMENT;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_STASH;
@@ -31,11 +32,8 @@
import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import static com.android.quickstep.util.SystemUiFlagUtils.isTaskbarHidden;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_COMMUNAL_HUB_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK;
-import static com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_AWAKE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -192,6 +190,8 @@
private boolean mIsQsbInline;
+ private boolean mIsHotseatVisibleForTaskbarAlignment;
+
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
new DeviceProfile.OnDeviceProfileChangeListener() {
@Override
@@ -378,16 +378,7 @@
updateStateForFlag(FLAG_DEVICE_LOCKED, SystemUiFlagUtils.isLocked(systemUiStateFlags));
- // Taskbar is hidden whenever the device is dreaming. The dreaming state includes the
- // interactive dreams, AoD, screen off. Since the SYSUI_STATE_DEVICE_DREAMING only kicks in
- // when the device is asleep, the second condition extends ensures that the transition from
- // and to the WAKEFULNESS_ASLEEP state also hide the taskbar, and improves the taskbar
- // hide/reveal animation timings. The Taskbar can show when dreaming if the glanceable hub
- // is showing on top.
- boolean isTaskbarHidden = (hasAnyFlag(systemUiStateFlags, SYSUI_STATE_DEVICE_DREAMING)
- && !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_COMMUNAL_HUB_SHOWING))
- || (systemUiStateFlags & SYSUI_STATE_WAKEFULNESS_MASK) != WAKEFULNESS_AWAKE;
- updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden);
+ updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden(systemUiStateFlags));
if (applyState) {
applyState();
@@ -682,7 +673,11 @@
+ mIconAlignment.value
+ " -> " + toAlignment + ": " + duration);
}
- animatorSet.play(iconAlignAnim);
+ if (hasAnyFlag(FLAG_TASKBAR_HIDDEN)) {
+ iconAlignAnim.setInterpolator(FINAL_FRAME);
+ } else {
+ animatorSet.play(iconAlignAnim);
+ }
}
Interpolator interpolator = enableScalingRevealHomeAnimation()
@@ -738,6 +733,7 @@
boolean committed) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
TaskbarStashController stashController = mControllers.taskbarStashController;
+ TaskbarInsetsController insetsController = mControllers.taskbarInsetsController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
Animator stashAnimator = stashController.createApplyStateAnimator(duration);
if (stashAnimator != null) {
@@ -746,7 +742,11 @@
public void onAnimationEnd(Animator animation) {
if (isInStashedState && committed) {
// Reset hotseat alpha to default
- mLauncher.getHotseat().setIconsAlpha(1, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
+ updateIconAlphaForHome(
+ /* taskbarAlpha = */ 0,
+ ALPHA_CHANNEL_TASKBAR_ALIGNMENT,
+ /* updateTaskbarAlpha = */ false
+ );
}
}
@@ -871,6 +871,14 @@
if (mIsQsbInline) {
mLauncher.getHotseat().setQsbAlpha(targetAlpha, alphaChannel);
}
+ if (alphaChannel == ALPHA_CHANNEL_TASKBAR_ALIGNMENT) {
+ boolean isHotseatVisibleForTaskbarAlignment = isHotseatVisibleForTaskbarAlignment();
+ if (mIsHotseatVisibleForTaskbarAlignment != isHotseatVisibleForTaskbarAlignment) {
+ mIsHotseatVisibleForTaskbarAlignment = isHotseatVisibleForTaskbarAlignment;
+ mControllers.taskbarInsetsController
+ .onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+ }
+ }
}
/** Updates launcher home screen appearance accordingly to the bubble bar location. */
@@ -878,19 +886,18 @@
mBubbleBarLocation = location;
if (location == null) {
// bubble bar is not present, hence no location, resetting the hotseat
- updateHotseatAndQsbTranslationX(0, animate);
+ updateHotseatAndQsbTranslationX(/* targetValue = */ 0, animate);
mBubbleBarLocation = null;
return;
}
DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
- if (!deviceProfile.shouldAdjustHotseatOnBubblesLocationUpdate(
+ if (!deviceProfile.shouldAdjustHotseatOnNavBarLocationUpdate(
mControllers.taskbarActivityContext)) {
return;
}
- boolean isRtl = isRtl(mLauncher.getResources());
- boolean isBubblesOnLeft = location.isOnLeft(isRtl);
+ boolean isBubblesOnLeft = location.isOnLeft(isRtl(mLauncher.getResources()));
int targetX = deviceProfile
- .getHotseatTranslationXForBubbleBar(isBubblesOnLeft, isRtl);
+ .getHotseatTranslationXForNavBar(mLauncher, isBubblesOnLeft);
updateHotseatAndQsbTranslationX(targetX, animate);
}
@@ -932,6 +939,13 @@
translationXAnimation.start();
}
+ /** Returns true if hotseat icons visible for the taskbar alignment */
+ public boolean isHotseatVisibleForTaskbarAlignment() {
+ return mLauncher.getHotseat()
+ .getIconsAlpha(ALPHA_CHANNEL_TASKBAR_ALIGNMENT).getValue() == 1;
+ }
+
+
private final class TaskBarRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 78e7b47..c18cf28 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -16,7 +16,6 @@
package com.android.launcher3.taskbar;
import static android.content.Context.RECEIVER_NOT_EXPORTED;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -72,7 +71,7 @@
import com.android.quickstep.AllAppsActionManager;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.AssistUtils;
+import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -220,7 +219,7 @@
TaskbarNavButtonCallbacks navCallbacks,
@NonNull DesktopVisibilityController desktopVisibilityController) {
Display display =
- context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+ context.getSystemService(DisplayManager.class).getDisplay(context.getDisplayId());
mContext = context.createWindowContext(display,
ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL,
null);
@@ -250,7 +249,7 @@
SystemUiProxy.INSTANCE.get(mContext),
ContextualEduStatsManager.INSTANCE.get(mContext),
new Handler(),
- AssistUtils.newInstance(mContext));
+ ContextualSearchInvoker.newInstance(mContext));
mComponentCallbacks = new ComponentCallbacks() {
private Configuration mOldConfig = mContext.getResources().getConfiguration();
@@ -672,11 +671,6 @@
@VisibleForTesting
public void setSuspended(boolean isSuspended) {
mIsSuspended = isSuspended;
- if (mIsSuspended) {
- removeTaskbarRootViewFromWindow();
- } else {
- addTaskbarRootViewToWindow();
- }
}
private void addTaskbarRootViewToWindow() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 15c35b6..8947914 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -51,7 +51,7 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
-import com.android.quickstep.util.AssistUtils;
+import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -113,7 +113,7 @@
private final SystemUiProxy mSystemUiProxy;
private final ContextualEduStatsManager mContextualEduStatsManager;
private final Handler mHandler;
- private final AssistUtils mAssistUtils;
+ private final ContextualSearchInvoker mContextualSearchInvoker;
@Nullable private StatsLogManager mStatsLogManager;
private final Runnable mResetLongPress = this::resetScreenUnpin;
@@ -124,13 +124,13 @@
SystemUiProxy systemUiProxy,
ContextualEduStatsManager contextualEduStatsManager,
Handler handler,
- AssistUtils assistUtils) {
+ ContextualSearchInvoker contextualSearchInvoker) {
mContext = context;
mCallbacks = callbacks;
mSystemUiProxy = systemUiProxy;
mContextualEduStatsManager = contextualEduStatsManager;
mHandler = handler;
- mAssistUtils = assistUtils;
+ mContextualSearchInvoker = contextualSearchInvoker;
}
public void onButtonClick(@TaskbarButton int buttonType, View view) {
@@ -344,8 +344,9 @@
if (mScreenPinned || !mAssistantLongPressEnabled) {
return;
}
- // Attempt to start Assist with AssistUtils, otherwise fall back to SysUi's implementation.
- if (!mAssistUtils.tryStartAssistOverride(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
+ // Attempt to start Contextual Search, otherwise fall back to SysUi's implementation.
+ if (!mContextualSearchInvoker.tryStartAssistOverride(
+ INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
Bundle args = new Bundle();
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
mSystemUiProxy.startAssistant(args);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
index 1867cd0..7848b7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -76,10 +76,10 @@
}
}
- fun showPinningView(view: View) {
+ fun showPinningView(view: View, horizontalPosition: Float = -1f) {
context.isTaskbarWindowFullscreen = true
view.post {
- val popupView = getPopupView(view)
+ val popupView = getPopupView(view, horizontalPosition)
popupView.requestFocus()
popupView.onCloseCallback = onCloseCallback
context.onPopupVisibilityChanged(true)
@@ -89,8 +89,8 @@
}
@VisibleForTesting
- fun getPopupView(view: View): TaskbarDividerPopupView<*> {
- return createAndPopulate(view, context)
+ fun getPopupView(view: View, horizontalPosition: Float = -1f): TaskbarDividerPopupView<*> {
+ return createAndPopulate(view, context, horizontalPosition)
}
@VisibleForTesting
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 9c34ff0..3e8b615 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -251,7 +251,7 @@
// Remove any newly-missing Tasks, and actual group-tasks
val newShownTasks =
shownTasks
- .filter { !it.hasMultipleTasks() }
+ .filter { !it.supportsMultipleTasks() }
.filter { it.task1.key.id in desktopTaskIds }
.toMutableList()
// Add any new Tasks, maintaining the order from previous shownTasks.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 8991965..b19da6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -30,6 +30,7 @@
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
+import static com.android.quickstep.util.SystemUiFlagUtils.isTaskbarHidden;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
@@ -104,6 +105,7 @@
// An internal no-op flag to determine whether we should delay the taskbar background animation
private static final int FLAG_DELAY_TASKBAR_BG_TAG = 1 << 12;
public static final int FLAG_STASHED_FOR_BUBBLES = 1 << 13; // show handle for stashed hotseat
+ public static final int FLAG_TASKBAR_HIDDEN = 1 << 14; // taskbar hidden during dream, etc...
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -215,6 +217,13 @@
*/
private static final int TRANSITION_UNSTASH_SUW_MANUAL = 3;
+ /**
+ * total duration of entering dream state animation, which we use as start delay to
+ * applyState() when SYSUI_STATE_DEVICE_DREAMING flag is present. Keep this in sync with
+ * DreamAnimationController.TOTAL_ANIM_DURATION.
+ */
+ private static final int SKIP_TOTAL_DREAM_ANIM_DURATION = 450;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
TRANSITION_DEFAULT,
@@ -1123,7 +1132,13 @@
startDelay = getTaskbarStashStartDelayForIme();
}
- applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
+ if (isTaskbarHidden(systemUiStateFlags) && !hasAnyFlag(FLAG_TASKBAR_HIDDEN)) {
+ updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden(systemUiStateFlags));
+ applyState(0, SKIP_TOTAL_DREAM_ANIM_DURATION);
+ } else {
+ updateStateForFlag(FLAG_TASKBAR_HIDDEN, isTaskbarHidden(systemUiStateFlags));
+ applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
+ }
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index b80aaf8..db69e8f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -75,8 +75,14 @@
mControllers = null;
}
- protected boolean isTaskbarTouchable() {
- return true;
+ /** Returns true if transition animation to launcher home is being played. */
+ public boolean isAnimatingToLauncher() {
+ return false;
+ }
+
+ /** Returns true if hotseat icons visible for the taskbar alignment. */
+ public boolean isHotseatVisibleForTaskBarAlignment() {
+ return false;
}
/**
@@ -91,6 +97,14 @@
protected void onStashedInAppChanged() { }
/**
+ * Whether the Taskbar should use in-app layout.
+ * @return {@code true} iff in-app display progress > 0 or Launcher Activity paused.
+ */
+ public boolean shouldUseInAppLayout() {
+ return false;
+ }
+
+ /**
* Called when taskbar icon layout bounds change.
*/
protected void onIconLayoutBoundsChanged() { }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 8763509..07ec135 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -65,8 +65,8 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconButtonView;
-import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
@@ -185,20 +185,50 @@
resources.getDrawable(R.drawable.taskbar_overflow_icon));
mTaskbarOverflowView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
}
- // TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
mMaxNumIcons = calculateMaxNumIcons();
}
/**
- // @return the maximum number of 'icons' that can fit in the taskbar.
- // TODO(368119679): Assumes that they are all the same size.
+ * @return the maximum number of 'icons' that can fit in the taskbar.
*/
private int calculateMaxNumIcons() {
- int availableWidth = mActivityContext.getDeviceProfile().widthPx
- - (mActivityContext.getDeviceProfile().edgeMarginPx * 2);
- return Math.floorDiv(availableWidth, mIconTouchSize);
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ int availableWidth = deviceProfile.widthPx;
+
+ // Reserve space required for edge margins, or for navbar if shown. If task bar needs to be
+ // center aligned with nav bar shown, reserve space on both sides.
+ availableWidth -= Math.max(deviceProfile.edgeMarginPx, deviceProfile.hotseatBarEndOffset);
+ availableWidth -= Math.max(deviceProfile.edgeMarginPx,
+ mShouldTryStartAlign ? 0 : deviceProfile.hotseatBarEndOffset);
+
+ // The space taken by an item icon used during layout.
+ int iconSize = 2 * mItemMarginLeftRight + mIconTouchSize;
+
+ int additionalIcons = 0;
+
+ if (mTaskbarDividerContainer != null) {
+ // Space for divider icon is reduced during layout compared to normal icon size, reserve
+ // space for the divider separately.
+ availableWidth -= iconSize - 4 * mItemMarginLeftRight;
+ ++additionalIcons;
+ }
+
+ // All apps icon takes less space compared to normal icon size, reserve space for the icon
+ // separately.
+ if (mAllAppsButtonContainer != null) {
+ boolean forceTransientTaskbarSize =
+ enableTaskbarPinning() && !mActivityContext.isThreeButtonNav();
+ availableWidth -= iconSize - (int) getResources().getDimension(
+ mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
+ forceTransientTaskbarSize || (
+ DisplayController.isTransientTaskbar(mActivityContext)
+ && !mActivityContext.isPhoneMode())));
+ ++additionalIcons;
+ }
+
+ return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
}
@Override
@@ -286,7 +316,8 @@
if (mAllAppsButtonContainer != null) {
mAllAppsButtonContainer.setUpCallbacks(callbacks);
}
- if (mTaskbarDividerContainer != null && callbacks.supportsDividerLongPress()) {
+ if (mTaskbarDividerContainer != null
+ && mActivityContext.getTaskbarFeatureEvaluator().getSupportsPinningPopup()) {
mTaskbarDividerContainer.setUpCallbacks(callbacks);
}
if (mTaskbarOverflowView != null) {
@@ -295,6 +326,10 @@
mTaskbarOverflowView.setOnLongClickListener(
mControllerCallbacks.getOverflowOnLongClickListener());
}
+ if (Flags.showTaskbarPinningPopupFromAnywhere()
+ && mActivityContext.getTaskbarFeatureEvaluator().getSupportsPinningPopup()) {
+ setOnTouchListener(mControllerCallbacks.getTaskbarTouchListener());
+ }
}
private void removeAndRecycle(View view) {
@@ -412,18 +447,38 @@
if (mTaskbarDividerContainer != null && !recentTasks.isEmpty()) {
addView(mTaskbarDividerContainer, nextViewIndex++);
mAddedDividerForRecents = true;
- if (mTaskbarOverflowView != null) {
+ }
+
+ // At this point, the all apps button has not been added as a child view, but needs to be
+ // accounted for when comparing current icon count to max number of icons.
+ int nonTaskIconsToBeAdded = 1;
+
+ boolean supportsOverflow = Flags.taskbarOverflow();
+ if (supportsOverflow) {
+ int numberOfSupportedRecents = 0;
+ for (GroupTask task : recentTasks) {
+ // TODO(b/343289567 and b/316004172): support app pairs and desktop mode.
+ if (!task.hasMultipleTasks()) {
+ ++numberOfSupportedRecents;
+ }
+ }
+ if (nextViewIndex + numberOfSupportedRecents + nonTaskIconsToBeAdded > mMaxNumIcons
+ && mTaskbarOverflowView != null) {
addView(mTaskbarOverflowView, nextViewIndex++);
}
}
// Add Recent/Running icons.
for (GroupTask task : recentTasks) {
+ if (supportsOverflow && nextViewIndex + nonTaskIconsToBeAdded >= mMaxNumIcons) {
+ break;
+ }
+
// Replace any Recent views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
boolean isCollection = false;
- if (task.hasMultipleTasks()) {
- if (task instanceof DesktopTask) {
+ if (task.supportsMultipleTasks()) {
+ if (task.taskViewType == TaskViewType.DESKTOP) {
// TODO(b/316004172): use Desktop tile layout.
expectedLayoutResId = -1;
} else {
@@ -488,8 +543,6 @@
}
}
- updateRecentAppsToFit();
-
if (mActivityContext.getDeviceProfile().isQsbInline) {
addView(mQsb, mIsRtl ? getChildCount() : 0);
// Always set QSB to invisible after re-adding.
@@ -497,45 +550,6 @@
}
}
- /**
- * Updates the recent apps portion of the taskbar by:
- * - Removing overflow affordance if overflow is not needed.
- * - Removing any recent apps that do not fit.
- */
- private void updateRecentAppsToFit() {
- if (!Flags.taskbarOverflow()) {
- return;
- }
- int indexOfFirstRecentApp = -1;
- int size = getChildCount();
- boolean removeOverflowView = true;
-
- for (int i = 0; i < size; ++i) {
- if (getChildAt(i).getTag() instanceof GroupTask) {
- indexOfFirstRecentApp = i;
- removeOverflowView = false;
- break;
- }
- }
-
- if (indexOfFirstRecentApp != -1) {
- // We pre-maturely added the overflow icon, so we can take it out of the count.
- int numRecentAppsToRemove = Math.max(0, getChildCount() - mMaxNumIcons + 1);
- if (numRecentAppsToRemove <= 1) {
- // We can fit all of the recent apps if we remove the overflow icon.
- removeOverflowView = true;
- } else {
- for (int i = 0; i < numRecentAppsToRemove; ++i) {
- removeAndRecycle(getChildAt(indexOfFirstRecentApp));
- }
- }
- }
-
- if (removeOverflowView) {
- removeView(mTaskbarOverflowView);
- }
- }
-
/** Binds the GroupTask to the BubbleTextView to be ready to present to the user. */
public void applyGroupTaskToBubbleTextView(BubbleTextView btv, GroupTask groupTask) {
// TODO(b/343289567): support app pairs.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 176be1c..4591f9b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -19,14 +19,19 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.jank.Cuj;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
+import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
@@ -39,12 +44,14 @@
private final TaskbarActivityContext mActivity;
private final TaskbarControllers mControllers;
private final TaskbarView mTaskbarView;
+ private final GestureDetector mGestureDetector;
public TaskbarViewCallbacks(TaskbarActivityContext activity, TaskbarControllers controllers,
TaskbarView taskbarView) {
mActivity = activity;
mControllers = controllers;
mTaskbarView = taskbarView;
+ mGestureDetector = new GestureDetector(activity, new TaskbarViewGestureListener());
}
public View.OnClickListener getIconOnClickListener() {
@@ -64,27 +71,28 @@
mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS);
}
- public boolean isAllAppsButtonHapticFeedbackEnabled() {
+ /** @return true if haptic feedback should occur when long pressing the all apps button. */
+ public boolean isAllAppsButtonHapticFeedbackEnabled(Context context) {
return false;
}
+ @SuppressLint("ClickableViewAccessibility")
+ public View.OnTouchListener getTaskbarTouchListener() {
+ return (view, event) -> mGestureDetector.onTouchEvent(event);
+ }
+
public View.OnLongClickListener getTaskbarDividerLongClickListener() {
return v -> {
- mControllers.taskbarPinningController.showPinningView(v);
+ mControllers.taskbarPinningController.showPinningView(v, getDividerCenterX());
return true;
};
}
- /** Check to see if we support long press on taskbar divider */
- public boolean supportsDividerLongPress() {
- return !mActivity.isThreeButtonNav();
- }
-
public View.OnTouchListener getTaskbarDividerRightClickListener() {
return (v, event) -> {
if (event.isFromSource(InputDevice.SOURCE_MOUSE)
&& event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
- mControllers.taskbarPinningController.showPinningView(v);
+ mControllers.taskbarPinningController.showPinningView(v, getDividerCenterX());
return true;
}
return false;
@@ -157,4 +165,32 @@
}
};
}
+
+ private float getDividerCenterX() {
+ View divider = mTaskbarView.getTaskbarDividerViewContainer();
+ if (divider == null) {
+ return 0.0f;
+ }
+ return divider.getX() + (float) divider.getWidth() / 2;
+ }
+
+ private class TaskbarViewGestureListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onDown(@NonNull MotionEvent event) {
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapUp(@NonNull MotionEvent event) {
+ return true;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent event) {
+ if (DisplayController.isPinnedTaskbar(mActivity)) {
+ mControllers.taskbarPinningController.showPinningView(mTaskbarView,
+ event.getRawX());
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt
index ba0f5a0..704d6cf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt
@@ -16,10 +16,14 @@
package com.android.launcher3.taskbar
+import android.app.contextualsearch.ContextualSearchManager.ENTRYPOINT_LONG_PRESS_META
import android.content.Context
import com.android.launcher3.R
+import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.util.ResourceBasedOverride
import com.android.launcher3.util.ResourceBasedOverride.Overrides
+import com.android.quickstep.TopTaskTracker
+import com.android.quickstep.util.ContextualSearchInvoker
/** Creates [TaskbarViewCallbacks] instances. */
open class TaskbarViewCallbacksFactory : ResourceBasedOverride {
@@ -28,7 +32,35 @@
activity: TaskbarActivityContext,
controllers: TaskbarControllers,
taskbarView: TaskbarView,
- ): TaskbarViewCallbacks = TaskbarViewCallbacks(activity, controllers, taskbarView)
+ ): TaskbarViewCallbacks {
+ return object : TaskbarViewCallbacks(activity, controllers, taskbarView) {
+ override fun triggerAllAppsButtonLongClick() {
+ super.triggerAllAppsButtonLongClick()
+
+ val contextualSearchInvoked =
+ ContextualSearchInvoker.newInstance(activity).show(ENTRYPOINT_LONG_PRESS_META)
+ if (contextualSearchInvoked) {
+ val runningPackage =
+ TopTaskTracker.INSTANCE[activity].getCachedTopTask(
+ /* filterOnlyVisibleRecents */ true
+ )
+ .getPackageName()
+ activity.statsLogManager
+ .logger()
+ .withPackageName(runningPackage)
+ .log(StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_META)
+ }
+ }
+
+ override fun isAllAppsButtonHapticFeedbackEnabled(context: Context): Boolean {
+ return longPressAllAppsToStartContextualSearch(context)
+ }
+ }
+ }
+
+ open fun longPressAllAppsToStartContextualSearch(context: Context): Boolean =
+ ContextualSearchInvoker.newInstance(context)
+ .runContextualSearchInvocationChecksAndLogFailures()
companion object {
@JvmStatic
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index bc61c72..e9458ff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.Utilities.isRtl;
import static com.android.launcher3.Utilities.mapRange;
import static com.android.launcher3.anim.AnimatedFloat.VALUE;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
@@ -234,7 +233,7 @@
mTaskbarNavButtonTranslationY =
controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
mTaskbarNavButtonTranslationYForInAppDisplay = controllers.navbarButtonsViewController
- .getTaskbarNavButtonTranslationYForInAppDisplay();
+ .getNavButtonTranslationYForInAppDisplay();
mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
@@ -838,12 +837,11 @@
int firstRecentTaskIndex = -1;
int hotseatNavBarTranslationX = 0;
- if (mCurrentBubbleBarLocation != null
- && taskbarDp.shouldAdjustHotseatOnBubblesLocationUpdate(mActivity)) {
- boolean isRtl = mTaskbarView.isLayoutRtl();
- boolean isBubblesOnLeft = mCurrentBubbleBarLocation.isOnLeft(isRtl);
+ if (mCurrentBubbleBarLocation != null) {
+ boolean isBubblesOnLeft = mCurrentBubbleBarLocation
+ .isOnLeft(mTaskbarView.isLayoutRtl());
hotseatNavBarTranslationX = taskbarDp
- .getHotseatTranslationXForBubbleBar(isBubblesOnLeft, isRtl);
+ .getHotseatTranslationXForNavBar(mActivity, isBubblesOnLeft);
}
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
@@ -938,10 +936,12 @@
mTaskbarView.isDividerForRecents(), recentTaskIndex);
if (positionInHotseat == ERROR_POSITION_IN_HOTSEAT_NOT_FOUND) continue;
- float hotseatAdjustedBorderSpace =
- launcherDp.getHotseatAdjustedBorderSpaceForBubbleBar(child.getContext());
+
float hotseatIconCenter;
- if (bubbleBarHasBubbles() && hotseatAdjustedBorderSpace != 0) {
+ if (launcherDp.shouldAdjustHotseatForBubbleBar(child.getContext(),
+ bubbleBarHasBubbles())) {
+ float hotseatAdjustedBorderSpace =
+ launcherDp.getHotseatAdjustedBorderSpaceForBubbleBar(child.getContext());
hotseatIconCenter = hotseatPadding.left + hotseatCellSize
+ (hotseatCellSize + hotseatAdjustedBorderSpace) * positionInHotseat
+ hotseatCellSize / 2f;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 69e1d43..63f101f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -43,6 +43,7 @@
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
+import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.MultiPropertyFactory;
@@ -117,6 +118,8 @@
public boolean mOverflowAdded;
private BubbleBarViewAnimator mBubbleBarViewAnimator;
+ private final FrameLayout mBubbleBarContainer;
+ private BubbleBarFlyoutController mBubbleBarFlyoutController;
private final TimeSource mTimeSource = System::currentTimeMillis;
@@ -127,6 +130,7 @@
FrameLayout bubbleBarContainer) {
mActivity = activity;
mBarView = barView;
+ mBubbleBarContainer = bubbleBarContainer;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
mIconSize = activity.getResources().getDimensionPixelSize(
@@ -141,6 +145,8 @@
mBubbleDragController = bubbleControllers.bubbleDragController;
mTaskbarStashController = controllers.taskbarStashController;
mTaskbarInsetsController = controllers.taskbarInsetsController;
+ mBubbleBarFlyoutController = new BubbleBarFlyoutController(
+ mBubbleBarContainer, createFlyoutPositioner(), createFlyoutTopBoundaryListener());
mBubbleBarViewAnimator = new BubbleBarViewAnimator(
mBarView, mBubbleStashController, mBubbleBarController::showExpandedView);
mTaskbarViewPropertiesProvider = taskbarViewPropertiesProvider;
@@ -266,6 +272,21 @@
};
}
+ private BubbleBarFlyoutController.TopBoundaryListener createFlyoutTopBoundaryListener() {
+ return new BubbleBarFlyoutController.TopBoundaryListener() {
+ @Override
+ public void extendTopBoundary(int space) {
+ int defaultSize = mActivity.getDefaultTaskbarWindowSize();
+ mActivity.setTaskbarWindowSize(defaultSize + space);
+ }
+
+ @Override
+ public void resetTopBoundary() {
+ mActivity.setTaskbarWindowSize(mActivity.getDefaultTaskbarWindowSize());
+ }
+ };
+ }
+
private void onBubbleClicked(BubbleView bubbleView) {
bubbleView.markSeen();
BubbleBarItem bubble = bubbleView.getBubble();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
index 49760ff..c431deb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -21,20 +21,30 @@
import android.widget.FrameLayout
import androidx.core.animation.ValueAnimator
import com.android.launcher3.R
+import com.android.systemui.util.doOnEnd
+import com.android.systemui.util.doOnStart
/** Creates and manages the visibility of the [BubbleBarFlyoutView]. */
-class BubbleBarFlyoutController(
+class BubbleBarFlyoutController
+@JvmOverloads
+constructor(
private val container: FrameLayout,
private val positioner: BubbleBarFlyoutPositioner,
+ private val topBoundaryListener: TopBoundaryListener,
+ private val flyoutScheduler: FlyoutScheduler = HandlerScheduler(container),
) {
+ private companion object {
+ const val EXPAND_COLLAPSE_ANIMATION_DURATION_MS = 250L
+ }
+
private var flyout: BubbleBarFlyoutView? = null
private val horizontalMargin =
container.context.resources.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)
fun setUpFlyout(message: BubbleBarFlyoutMessage) {
flyout?.let(container::removeView)
- val flyout = BubbleBarFlyoutView(container.context, positioner)
+ val flyout = BubbleBarFlyoutView(container.context, positioner, flyoutScheduler)
flyout.translationY = positioner.targetTy
@@ -48,17 +58,43 @@
lp.marginEnd = horizontalMargin
container.addView(flyout, lp)
- val animator = ValueAnimator.ofFloat(0f, 1f)
+ val animator =
+ ValueAnimator.ofFloat(0f, 1f).setDuration(EXPAND_COLLAPSE_ANIMATION_DURATION_MS)
animator.addUpdateListener { _ ->
flyout.updateExpansionProgress(animator.animatedValue as Float)
}
+ animator.doOnStart {
+ val flyoutTop = flyout.top + flyout.translationY
+ // If the top position of the flyout is negative, then it's bleeding over the
+ // top boundary of its parent view
+ if (flyoutTop < 0) topBoundaryListener.extendTopBoundary(space = -flyoutTop.toInt())
+ }
flyout.showFromCollapsed(message) { animator.start() }
this.flyout = flyout
}
- fun hideFlyout() {
+ fun hideFlyout(endAction: () -> Unit) {
val flyout = this.flyout ?: return
- container.removeView(flyout)
- this.flyout = null
+ val animator =
+ ValueAnimator.ofFloat(1f, 0f).setDuration(EXPAND_COLLAPSE_ANIMATION_DURATION_MS)
+ animator.addUpdateListener { _ ->
+ flyout.updateExpansionProgress(animator.animatedValue as Float)
+ }
+ animator.doOnEnd {
+ container.removeView(flyout)
+ this@BubbleBarFlyoutController.flyout = null
+ topBoundaryListener.resetTopBoundary()
+ endAction()
+ }
+ animator.start()
+ }
+
+ /** Notifies when the top boundary of the flyout view changes. */
+ interface TopBoundaryListener {
+ /** Requests to extend the top boundary of the parent to fully include the flyout. */
+ fun extendTopBoundary(space: Int)
+
+ /** Resets the top boundary of the parent. */
+ fun resetTopBoundary()
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 2022a42..c60fba2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -36,14 +36,18 @@
import com.android.launcher3.popup.RoundedArrowDrawable
/** The flyout view used to notify the user of a new bubble notification. */
-class BubbleBarFlyoutView(context: Context, private val positioner: BubbleBarFlyoutPositioner) :
- ConstraintLayout(context) {
+class BubbleBarFlyoutView(
+ context: Context,
+ private val positioner: BubbleBarFlyoutPositioner,
+ scheduler: FlyoutScheduler? = null,
+) : ConstraintLayout(context) {
private companion object {
// the minimum progress of the expansion animation before the content starts fading in.
const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f
}
+ private val scheduler: FlyoutScheduler = scheduler ?: HandlerScheduler(this)
private val title: TextView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_title) }
@@ -197,11 +201,10 @@
// post the request to start the expand animation to the looper so the view can measure
// itself
- post(expandAnimation)
+ scheduler.runAfterLayout(expandAnimation)
}
private fun setData(flyoutMessage: BubbleBarFlyoutMessage) {
- // the avatar is only displayed in group chat messages
if (flyoutMessage.icon != null) {
icon.visibility = VISIBLE
icon.setImageDrawable(flyoutMessage.icon)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/FlyoutScheduler.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/FlyoutScheduler.kt
new file mode 100644
index 0000000..6f5d700
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/FlyoutScheduler.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.bubbles.flyout
+
+import android.view.View
+
+/** Interface for scheduling jobs by flyout. */
+fun interface FlyoutScheduler {
+ /** Runs the given [block] after layout. */
+ fun runAfterLayout(block: () -> Unit)
+}
+
+/** A [FlyoutScheduler] that uses a Handler to schedule jobs. */
+class HandlerScheduler(val view: View) : FlyoutScheduler {
+ override fun runAfterLayout(block: () -> Unit) {
+ view.post(block)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index e6c0b2f..c5f8aa0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -37,7 +37,7 @@
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
import com.android.quickstep.DeviceConfigWrapper
-import com.android.quickstep.util.AssistStateManager
+import com.android.quickstep.util.ContextualSearchStateManager
/** Taskbar all apps button container for customizable taskbar. */
class TaskbarAllAppsButtonContainer
@@ -79,17 +79,18 @@
setOnClickListener(this::onAllAppsButtonClick)
setOnLongClickListener(this::onAllAppsButtonLongClick)
setOnTouchListener(this::onAllAppsButtonTouch)
- isHapticFeedbackEnabled = taskbarViewCallbacks.isAllAppsButtonHapticFeedbackEnabled()
+ isHapticFeedbackEnabled =
+ taskbarViewCallbacks.isAllAppsButtonHapticFeedbackEnabled(mContext)
allAppsTouchRunnable = Runnable {
taskbarViewCallbacks.triggerAllAppsButtonLongClick()
allAppsTouchTriggered = true
}
- val assistStateManager = AssistStateManager.INSTANCE[mContext]
+ val contextualSearchStateManager = ContextualSearchStateManager.INSTANCE[mContext]
if (
DeviceConfigWrapper.get().customLpaaThresholds &&
- assistStateManager.lpnhDurationMillis.isPresent
+ contextualSearchStateManager.lpnhDurationMillis.isPresent
) {
- allAppsButtonTouchDelayMs = assistStateManager.lpnhDurationMillis.get()
+ allAppsButtonTouchDelayMs = contextualSearchStateManager.lpnhDurationMillis.get()
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index 7739a0e..f130d29 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -23,9 +23,7 @@
/** Evaluates all the features taskbar can have. */
class TaskbarFeatureEvaluator
-private constructor(
- private val taskbarActivityContext: TaskbarActivityContext,
-) {
+private constructor(private val taskbarActivityContext: TaskbarActivityContext) {
val hasAllApps = true
val hasAppIcons = true
val hasBubbles = false
@@ -43,6 +41,9 @@
val isLandscape: Boolean
get() = taskbarActivityContext.deviceProfile.isLandscape
+ val supportsPinningPopup: Boolean
+ get() = !hasNavButtons
+
fun onDestroy() {
taskbarFeatureEvaluator = null
}
@@ -51,9 +52,7 @@
@Volatile private var taskbarFeatureEvaluator: TaskbarFeatureEvaluator? = null
@JvmStatic
- fun getInstance(
- taskbarActivityContext: TaskbarActivityContext,
- ): TaskbarFeatureEvaluator {
+ fun getInstance(taskbarActivityContext: TaskbarActivityContext): TaskbarFeatureEvaluator {
synchronized(this) {
if (taskbarFeatureEvaluator == null) {
taskbarFeatureEvaluator = TaskbarFeatureEvaluator(taskbarActivityContext)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 39bf6ac..4ad65e1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -419,10 +419,8 @@
mDepthController.setActivityStarted(isStarted());
}
- if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
- if (!FeatureFlags.enableHomeTransitionListener() && mTaskbarUIController != null) {
- mTaskbarUIController.onLauncherVisibilityChanged(hasBeenResumed());
- }
+ if ((changeBits & ACTIVITY_STATE_RESUMED) != 0 && mTaskbarUIController != null) {
+ mTaskbarUIController.onLauncherPausedOrResumed(isPaused());
}
super.onActivityFlagsChanged(changeBits);
@@ -1103,20 +1101,14 @@
if (isBubbleBarEnabled()
&& enableBubbleBarInPersistentTaskBar()
&& mBubbleBarLocation != null) {
- boolean isRtl = isRtl(getResources());
- boolean isBubblesOnLeft = mBubbleBarLocation.isOnLeft(isRtl);
+ boolean isBubblesOnLeft = mBubbleBarLocation.isOnLeft(isRtl(getResources()));
translationX += mDeviceProfile
- .getHotseatTranslationXForBubbleBar(isBubblesOnLeft, isRtl);
+ .getHotseatTranslationXForNavBar(this, isBubblesOnLeft);
}
- if (isBubbleBarEnabled() && hasBubbles()) {
- // TODO(368379159) : create a class to reuse computation logic
- float adjustedBorderSpace =
- mDeviceProfile.getHotseatAdjustedBorderSpaceForBubbleBar(this);
- if (Float.compare(adjustedBorderSpace, 0f) != 0) {
- float borderSpaceDelta = adjustedBorderSpace - mDeviceProfile.hotseatBorderSpace;
- translationX +=
- (int) (mDeviceProfile.iconSizePx + itemInfo.cellX * borderSpaceDelta);
- }
+ if (isBubbleBarEnabled()
+ && mDeviceProfile.shouldAdjustHotseatForBubbleBar(getContext(), hasBubbles())) {
+ translationX += (int) mDeviceProfile
+ .getHotseatAdjustedTranslation(getContext(), itemInfo.cellX);
}
return translationX;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapperImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapperImpl.java
index 74572c4..3aa1963 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapperImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapperImpl.java
@@ -27,6 +27,8 @@
import android.content.pm.ResolveInfo;
import com.android.launcher3.BuildConfig;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
@@ -34,7 +36,6 @@
import com.android.systemui.shared.plugins.PluginInstance;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
-import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -42,16 +43,17 @@
import java.util.List;
import java.util.Set;
-public class PluginManagerWrapperImpl extends PluginManagerWrapper {
+import javax.inject.Inject;
- private static final UncaughtExceptionPreHandlerManager UNCAUGHT_EXCEPTION_PRE_HANDLER_MANAGER =
- new UncaughtExceptionPreHandlerManager();
+@LauncherAppSingleton
+public class PluginManagerWrapperImpl extends PluginManagerWrapper {
private final Context mContext;
private final PluginManagerImpl mPluginManager;
private final PluginEnablerImpl mPluginEnabler;
- public PluginManagerWrapperImpl(Context c) {
+ @Inject
+ public PluginManagerWrapperImpl(@ApplicationContext Context c) {
mContext = c;
mPluginEnabler = new PluginEnablerImpl(c);
List<String> privilegedPlugins = Collections.emptyList();
@@ -64,9 +66,11 @@
c.getSystemService(NotificationManager.class), mPluginEnabler,
privilegedPlugins, instanceFactory);
+ // Use null preHandlerManager, as the handler is never unregistered which can cause leaks
+ // when using multiple dagger graphs.
mPluginManager = new PluginManagerImpl(c, instanceManagerFactory,
BuildConfig.IS_DEBUG_DEVICE,
- UNCAUGHT_EXCEPTION_PRE_HANDLER_MANAGER, mPluginEnabler,
+ null /* preHandlerManager */, mPluginEnabler,
new PluginPrefs(c), privilegedPlugins);
}
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index 7786353..143ef12 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -378,9 +378,6 @@
public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) {
out.x = dp.widthPx;
out.y = dp.heightPx;
- if (dp.isTablet && !DisplayController.isTransientTaskbar(context)) {
- out.y -= dp.taskbarHeight;
- }
}
/**
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
new file mode 100644
index 0000000..46c4f36
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.view.View
+import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.R
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.popup.SystemShortcut
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskContainer
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+
+/** A menu item that allows the user to move the current app into external display. */
+class ExternalDisplaySystemShortcut(
+ container: RecentsViewContainer,
+ abstractFloatingViewHelper: AbstractFloatingViewHelper,
+ private val taskContainer: TaskContainer,
+) :
+ SystemShortcut<RecentsViewContainer>(
+ R.drawable.ic_external_display,
+ R.string.recent_task_option_external_display,
+ container,
+ taskContainer.itemInfo,
+ taskContainer.taskView,
+ abstractFloatingViewHelper,
+ ) {
+ override fun onClick(view: View) {
+ dismissTaskMenuView()
+ val recentsView = mTarget.getOverviewPanel<RecentsView<*, *>>()
+ recentsView.moveTaskToExternalDisplay(taskContainer) {
+ mTarget.statsLogManager
+ .logger()
+ .withItemInfo(taskContainer.itemInfo)
+ .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_EXTERNAL_DISPLAY_TAP)
+ }
+ }
+
+ companion object {
+ @JvmOverloads
+ /**
+ * Creates a factory for creating move task to external display system shortcuts in
+ * [com.android.quickstep.TaskOverlayFactory].
+ */
+ fun createFactory(
+ abstractFloatingViewHelper: AbstractFloatingViewHelper = AbstractFloatingViewHelper()
+ ): TaskShortcutFactory =
+ object : TaskShortcutFactory {
+ override fun getShortcuts(
+ container: RecentsViewContainer,
+ taskContainer: TaskContainer,
+ ): List<ExternalDisplaySystemShortcut>? {
+ return if (
+ DesktopModeStatus.canEnterDesktopMode(container.asContext()) &&
+ Flags.moveToExternalDisplayShortcut()
+ )
+ listOf(
+ ExternalDisplaySystemShortcut(
+ container,
+ abstractFloatingViewHelper,
+ taskContainer,
+ )
+ )
+ else null
+ }
+
+ override fun showForGroupedTask() = true
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 2892d2c..015a449 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -36,6 +36,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.Flags;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
@@ -302,6 +303,16 @@
}
/**
+ * Requests that handling for this gesture should use a synthetic transition, as in that it
+ * will need to start a recents transition that is not backed by a system transition. This is
+ * generally only needed in scenarios where a system transition can not be created due to no
+ * changes in the WM hierarchy (ie. starting recents transition when you are already over home).
+ */
+ public boolean useSyntheticRecentsTransition() {
+ return mRunningTask.isHomeTask() && Flags.enableFallbackOverviewInWindow();
+ }
+
+ /**
* @return the running task for this gesture.
*/
@Nullable
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 461f963..e23947b 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -53,6 +53,7 @@
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import java.io.PrintWriter
import java.util.concurrent.ConcurrentLinkedDeque
+import java.util.concurrent.Executor
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@@ -69,6 +70,7 @@
private val overviewComponentObserver: OverviewComponentObserver,
private val taskAnimationManager: TaskAnimationManager,
private val dispatcherProvider: DispatcherProvider = ProductionDispatchers,
+ private val uiExecutor: Executor = Executors.MAIN_EXECUTOR,
) {
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatcherProvider.default)
@@ -85,7 +87,7 @@
get() = overviewComponentObserver.containerInterface
private val visibleRecentsView: RecentsView<*, *>?
- get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
+ get() = containerInterface.getVisibleRecentsView()
/**
* Adds a command to be executed next, after all pending tasks are completed. Max commands that
@@ -105,11 +107,7 @@
if (commandQueue.size == 1) {
Log.d(TAG, "execute: $command - queue size: ${commandQueue.size}")
- if (enableOverviewCommandHelperTimeout()) {
- coroutineScope.launch(dispatcherProvider.main) { processNextCommand() }
- } else {
- Executors.MAIN_EXECUTOR.execute { processNextCommand() }
- }
+ uiExecutor.execute { processNextCommand() }
} else {
Log.d(TAG, "not executed: $command - queue size: ${commandQueue.size}")
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index fc11812..7d5bd37 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -15,7 +15,9 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -29,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -102,7 +105,11 @@
long appCount = Arrays.stream(appTargets)
.filter(app -> app.mode == MODE_CLOSING)
.count();
- if (appCount == 0) {
+
+ boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
+ && app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
+ .count() > 0;
+ if (appCount == 0 && (!Flags.enableFallbackOverviewInWindow() || isOpeningHome)) {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
notifyAnimationCanceled();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 5131774..de8be50 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
@@ -70,7 +71,7 @@
import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.AssistStateManager;
+import com.android.quickstep.util.ContextualSearchStateManager;
import com.android.quickstep.util.GestureExclusionManager;
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener;
import com.android.quickstep.util.NavBarPosition;
@@ -101,7 +102,7 @@
private final DisplayController mDisplayController;
private final GestureExclusionManager mExclusionManager;
- private final AssistStateManager mAssistStateManager;
+ private final ContextualSearchStateManager mContextualSearchStateManager;
private final RotationTouchHelper mRotationTouchHelper;
private final TaskStackChangeListener mPipListener;
@@ -152,7 +153,7 @@
mContext = context;
mDisplayController = DisplayController.INSTANCE.get(context);
mExclusionManager = exclusionManager;
- mAssistStateManager = AssistStateManager.INSTANCE.get(context);
+ mContextualSearchStateManager = ContextualSearchStateManager.INSTANCE.get(context);
mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
if (isInstanceForTouches) {
@@ -563,6 +564,7 @@
return mAssistantAvailable
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
&& mRotationTouchHelper.touchInAssistantRegion(ev)
+ && !isTrackpadScroll(ev)
&& !isLockToAppActive();
}
@@ -617,8 +619,9 @@
: QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- if (mAssistStateManager.getLPNHCustomSlopMultiplier().isPresent()) {
- float customSlopMultiplier = mAssistStateManager.getLPNHCustomSlopMultiplier().get();
+ if (mContextualSearchStateManager.getLPNHCustomSlopMultiplier().isPresent()) {
+ float customSlopMultiplier =
+ mContextualSearchStateManager.getLPNHCustomSlopMultiplier().get();
return customSlopMultiplier * slopMultiplier * touchSlop;
} else {
return slopMultiplier * touchSlop;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 5f02893..c1d7ffa 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -64,7 +64,7 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
-import com.android.quickstep.util.AssistUtils;
+import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -311,8 +311,8 @@
setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
setUnfoldAnimationListener(mUnfoldAnimationListener);
setDesktopTaskListener(mDesktopTaskListener);
- setAssistantOverridesRequested(
- AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
+ setAssistantOverridesRequested(ContextualSearchInvoker.newInstance(mContext)
+ .getSysUiAssistOverrideInvocationTypes());
mStateChangeCallbacks.forEach(Runnable::run);
if (mUnfoldTransitionProvider != null) {
@@ -1079,16 +1079,6 @@
}
}
- public void removeFromSideStage(int taskId) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.removeFromSideStage(taskId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call removeFromSideStage");
- }
- }
- }
-
//
// One handed
//
@@ -1436,10 +1426,10 @@
/**
* If task with the given id is on the desktop, bring it to front
*/
- public void showDesktopApp(int taskId) {
+ public void showDesktopApp(int taskId, @Nullable RemoteTransition transition) {
if (mDesktopMode != null) {
try {
- mDesktopMode.showDesktopApp(taskId);
+ mDesktopMode.showDesktopApp(taskId, transition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call showDesktopApp", e);
}
@@ -1503,6 +1493,17 @@
}
}
+ /** Call shell to move a task with given `taskId` to external display. */
+ public void moveToExternalDisplay(int taskId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.moveToExternalDisplay(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call moveToExternalDisplay", e);
+ }
+ }
+ }
+
//
// Unfold transition
//
@@ -1534,7 +1535,7 @@
* Starts the recents activity. The caller should manage the thread on which this is called.
*/
public boolean startRecentsActivity(Intent intent, ActivityOptions options,
- RecentsAnimationListener listener) {
+ RecentsAnimationListener listener, boolean useSyntheticRecentsTransition) {
if (mRecentTasks == null) {
ActiveGestureProtoLogProxy.logRecentTasksMissing();
return false;
@@ -1565,6 +1566,9 @@
}
};
final Bundle optsBundle = options.toBundle();
+ if (useSyntheticRecentsTransition) {
+ optsBundle.putBoolean("is_synthetic_recents_transition", true);
+ }
try {
mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle,
mContext.getIApplicationThread(), runner);
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index bda292a..56c978a 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -296,8 +296,8 @@
// TODO:(b/365777482) if flag is enabled, but on launcher it will crash.
if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
&& Flags.enableFallbackOverviewInWindow()){
- mRecentsAnimationStartPending =
- getSystemUiProxy().startRecentsActivity(intent, options, mCallbacks);
+ mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
+ mCallbacks, gestureState.useSyntheticRecentsTransition());
mRecentsWindowsManager.startRecentsWindow(mCallbacks);
} else {
options.setPendingIntentBackgroundActivityStartMode(
@@ -326,9 +326,10 @@
});
}
- mRecentsAnimationStartPending = getSystemUiProxy()
- .startRecentsActivity(intent, options, mCallbacks);
+ mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent,
+ options, mCallbacks, false /* useSyntheticRecentsTransition */);
}
+
if (enableHandleDelayedGestureCallbacks()) {
ActiveGestureProtoLogProxy.logSettingRecentsAnimationStartPending(
mRecentsAnimationStartPending);
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 8e45767..0dbdcb7 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -116,6 +116,7 @@
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM,
DesktopSystemShortcut.Companion.createFactory(),
+ ExternalDisplaySystemShortcut.Companion.createFactory(),
TaskShortcutFactory.WELLBEING,
TaskShortcutFactory.SAVE_APP_PAIR,
TaskShortcutFactory.SCREENSHOT,
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4ee8747..1481ef2 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -122,8 +122,8 @@
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
-import com.android.quickstep.util.AssistStateManager;
-import com.android.quickstep.util.AssistUtils;
+import com.android.quickstep.util.ContextualSearchInvoker;
+import com.android.quickstep.util.ContextualSearchStateManager;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -297,7 +297,8 @@
@Override
public void onAssistantOverrideInvoked(int invocationType) {
executeForTouchInteractionService(tis -> {
- if (!AssistUtils.newInstance(tis).tryStartAssistOverride(invocationType)) {
+ if (!ContextualSearchInvoker.newInstance(tis)
+ .tryStartAssistOverride(invocationType)) {
Log.w(TAG, "Failed to invoke Assist override");
}
});
@@ -1645,8 +1646,8 @@
}
mTaskbarManager.dumpLogs("", pw);
mDesktopVisibilityController.dumpLogs("", pw);
- pw.println("AssistStateManager:");
- AssistStateManager.INSTANCE.get(this).dump("\t", pw);
+ pw.println("ContextualSearchStateManager:");
+ ContextualSearchStateManager.INSTANCE.get(this).dump("\t", pw);
SystemUiProxy.INSTANCE.get(this).dump(pw);
DeviceConfigWrapper.get().dump(" ", pw);
}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
index 08345b8..ab77a7f 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
@@ -15,8 +15,14 @@
*/
package com.android.quickstep.dagger;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl;
+import com.android.launcher3.util.PluginManagerWrapper;
+
+import dagger.Binds;
import dagger.Module;
@Module
-public class QuickStepModule {
+public abstract class QuickStepModule {
+
+ @Binds abstract PluginManagerWrapper bindPluginManagerWrapper(PluginManagerWrapperImpl impl);
}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index 341c868..977c036 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -19,7 +19,6 @@
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.model.WellbeingModel;
-import com.android.quickstep.logging.SettingsChangeLogger;
import com.android.quickstep.util.AsyncClockEventDelegate;
/**
@@ -31,7 +30,6 @@
* See {@link LauncherAppComponent} for the one actually used.
*/
public interface QuickstepBaseAppComponent extends LauncherBaseAppComponent {
- SettingsChangeLogger getSettingsChangeLogger();
WellbeingModel getWellbeingModel();
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index e15fa54..fbf671f 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -84,10 +84,7 @@
* [QuickstepProtoLogGroup.Constants.DEBUG_RECENTS_WINDOW]
*/
class RecentsWindowManager(context: Context) :
- RecentsWindowContext(context),
- RecentsViewContainer,
- StatefulContainer<RecentsState>,
- RecentsAnimationListener {
+ RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
companion object {
private const val HOME_APPEAR_DURATION: Long = 250
@@ -128,6 +125,17 @@
}
}
+ private val recentsAnimationListener =
+ object : RecentsAnimationListener {
+ override fun onRecentsAnimationCanceled(thumbnailDatas: HashMap<Int, ThumbnailData>) {
+ recentAnimationStopped()
+ }
+
+ override fun onRecentsAnimationFinished(controller: RecentsAnimationController) {
+ recentAnimationStopped()
+ }
+ }
+
init {
FallbackWindowInterface.init(this)
TaskStackChangeListeners.getInstance().registerTaskStackListener(taskStackChangeListener)
@@ -138,7 +146,7 @@
cleanupRecentsWindow()
FallbackWindowInterface.getInstance()?.destroy()
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(taskStackChangeListener)
- callbacks?.removeListener(this)
+ callbacks?.removeListener(recentsAnimationListener)
}
override fun startHome() {
@@ -203,7 +211,7 @@
windowManager.removeViewImmediate(windowView)
}
stateManager.moveToRestState()
- callbacks?.removeListener(this)
+ callbacks?.removeListener(recentsAnimationListener)
}
private fun isShowing(): Boolean {
@@ -249,17 +257,7 @@
onInitListener?.test(true)
this.callbacks = callbacks
- callbacks?.addListener(this)
- }
-
- override fun onRecentsAnimationCanceled(thumbnailDatas: HashMap<Int, ThumbnailData>) {
- super.onRecentsAnimationCanceled(thumbnailDatas)
- recentAnimationStopped()
- }
-
- override fun onRecentsAnimationFinished(controller: RecentsAnimationController) {
- super.onRecentsAnimationFinished(controller)
- recentAnimationStopped()
+ callbacks?.addListener(recentsAnimationListener)
}
private fun recentAnimationStopped() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
index 1d00e53..155d095 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -16,25 +16,65 @@
package com.android.quickstep.inputconsumers;
+import static android.app.contextualsearch.ContextualSearchManager.ENTRYPOINT_LONG_PRESS_NAV_HANDLE;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_ASSISTANT_SUCCESSFUL_NAV_HANDLE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OMNI_GET_LONG_PRESS_RUNNABLE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_OMNI_RUNNABLE;
+
import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.ViewConfiguration;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.VibratorWrapper;
+import com.android.quickstep.DeviceConfigWrapper;
import com.android.quickstep.NavHandle;
+import com.android.quickstep.TopTaskTracker;
+import com.android.quickstep.util.ContextualSearchHapticManager;
+import com.android.quickstep.util.ContextualSearchInvoker;
+import com.android.quickstep.util.ContextualSearchStateManager;
/**
* Class for extending nav handle long press behavior
*/
public class NavHandleLongPressHandler implements ResourceBasedOverride {
+ private static final String TAG = "NavHandleLongPressHandler";
+
+ protected final Context mContext;
+ protected final VibratorWrapper mVibratorWrapper;
+ protected final ContextualSearchHapticManager mContextualSearchHapticManager;
+ protected final ContextualSearchInvoker mContextualSearchInvoker;
+ protected final StatsLogManager mStatsLogManager;
+ private boolean mPendingInvocation;
+
+ public NavHandleLongPressHandler(Context context) {
+ mContext = context;
+ mStatsLogManager = StatsLogManager.newInstance(context);
+ mVibratorWrapper = VibratorWrapper.INSTANCE.get(mContext);
+ mContextualSearchHapticManager = ContextualSearchHapticManager.INSTANCE.get(context);
+ mContextualSearchInvoker = ContextualSearchInvoker.newInstance(mContext);
+ }
+
/** Creates NavHandleLongPressHandler as specified by overrides */
public static NavHandleLongPressHandler newInstance(Context context) {
return Overrides.getObject(NavHandleLongPressHandler.class, context,
R.string.nav_handle_long_press_handler_class);
}
+ protected boolean isContextualSearchEntrypointEnabled(NavHandle navHandle) {
+ return DeviceConfigWrapper.get().getEnableLongPressNavHandle();
+ }
+
/**
* Called when nav handle is long pressed to get the Runnable that should be executed by the
* caller to invoke long press behavior. If null is returned that means long press couldn't be
@@ -46,8 +86,48 @@
*
* @param navHandle to handle this long press
*/
- public @Nullable Runnable getLongPressRunnable(NavHandle navHandle) {
- return null;
+ @Nullable
+ @VisibleForTesting
+ final Runnable getLongPressRunnable(NavHandle navHandle) {
+ if (!isContextualSearchEntrypointEnabled(navHandle)) {
+ Log.i(TAG, "Contextual Search invocation failed: entry point disabled");
+ mVibratorWrapper.cancelVibrate();
+ return null;
+ }
+
+ if (!mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures()) {
+ Log.i(TAG, "Contextual Search invocation failed: precondition not satisfied");
+ mVibratorWrapper.cancelVibrate();
+ return null;
+ }
+
+ mPendingInvocation = true;
+ Log.i(TAG, "Contextual Search invocation: invocation runnable created");
+ InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+ mStatsLogManager.logger().withInstanceId(instanceId).log(
+ LAUNCHER_OMNI_GET_LONG_PRESS_RUNNABLE);
+ long startTimeMillis = SystemClock.elapsedRealtime();
+ return () -> {
+ mStatsLogManager.latencyLogger().withInstanceId(instanceId).withLatency(
+ SystemClock.elapsedRealtime() - startTimeMillis).log(
+ LAUNCHER_LATENCY_OMNI_RUNNABLE);
+ if (mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
+ ENTRYPOINT_LONG_PRESS_NAV_HANDLE)) {
+ Log.i(TAG, "Contextual Search invocation successful");
+
+ String runningPackage = TopTaskTracker.INSTANCE.get(mContext).getCachedTopTask(
+ /* filterOnlyVisibleRecents */ true).getPackageName();
+ mStatsLogManager.logger().withPackageName(runningPackage)
+ .log(LAUNCHER_LAUNCH_ASSISTANT_SUCCESSFUL_NAV_HANDLE);
+ } else {
+ mVibratorWrapper.cancelVibrate();
+ if (DeviceConfigWrapper.get().getAnimateLpnh()
+ && !DeviceConfigWrapper.get().getShrinkNavHandleOnPress()) {
+ navHandle.animateNavBarLongPress(
+ /*isTouchDown*/false, /*shrink*/ false, /*durationMs*/160);
+ }
+ }
+ };
}
/**
@@ -55,7 +135,15 @@
*
* @param navHandle to handle the animation for this touch
*/
- public void onTouchStarted(NavHandle navHandle) {}
+ @VisibleForTesting
+ final void onTouchStarted(NavHandle navHandle) {
+ mPendingInvocation = false;
+ if (isContextualSearchEntrypointEnabled(navHandle)
+ && mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures()) {
+ Log.i(TAG, "Contextual Search invocation: touch started");
+ startNavBarAnimation(navHandle);
+ }
+ }
/**
* Called when nav handle gesture is finished by the user lifting their finger or the system
@@ -64,5 +152,46 @@
* @param navHandle to handle the animation for this touch
* @param reason why the touch ended
*/
- public void onTouchFinished(NavHandle navHandle, String reason) {}
+ @VisibleForTesting
+ final void onTouchFinished(NavHandle navHandle, String reason) {
+ Log.i(TAG, "Contextual Search invocation: touch finished with reason: " + reason);
+
+ if (!DeviceConfigWrapper.get().getShrinkNavHandleOnPress() || !mPendingInvocation) {
+ mVibratorWrapper.cancelVibrate();
+ }
+
+ if (DeviceConfigWrapper.get().getAnimateLpnh()) {
+ if (DeviceConfigWrapper.get().getShrinkNavHandleOnPress()) {
+ navHandle.animateNavBarLongPress(
+ /*isTouchDown*/false, /*shrink*/ true, /*durationMs*/200);
+ } else {
+ navHandle.animateNavBarLongPress(
+ /*isTouchDown*/false, /*shrink*/ false, /*durationMs*/ 160);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ final void startNavBarAnimation(NavHandle navHandle) {
+ mContextualSearchHapticManager.vibrateForSearchHint();
+
+ if (DeviceConfigWrapper.get().getAnimateLpnh()) {
+ if (DeviceConfigWrapper.get().getShrinkNavHandleOnPress()) {
+ navHandle.animateNavBarLongPress(
+ /*isTouchDown*/ true, /*shrink*/true, /*durationMs*/200);
+ } else {
+ long longPressTimeout;
+ ContextualSearchStateManager contextualSearchStateManager =
+ ContextualSearchStateManager.INSTANCE.get(mContext);
+ if (contextualSearchStateManager.getLPNHDurationMillis().isPresent()) {
+ longPressTimeout =
+ contextualSearchStateManager.getLPNHDurationMillis().get().intValue();
+ } else {
+ longPressTimeout = ViewConfiguration.getLongPressTimeout();
+ }
+ navHandle.animateNavBarLongPress(
+ /*isTouchDown*/ true, /*shrink*/ false, /*durationMs*/ longPressTimeout);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index f4d3695..f5bef05e 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -38,7 +38,7 @@
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.TopTaskTracker;
-import com.android.quickstep.util.AssistStateManager;
+import com.android.quickstep.util.ContextualSearchStateManager;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -75,9 +75,11 @@
super(delegate, inputMonitor);
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
mDeepPressEnabled = DeviceConfigWrapper.get().getEnableLpnhDeepPress();
- AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(context);
- if (assistStateManager.getLPNHDurationMillis().isPresent()) {
- mLongPressTimeout = assistStateManager.getLPNHDurationMillis().get().intValue();
+ ContextualSearchStateManager contextualSearchStateManager =
+ ContextualSearchStateManager.INSTANCE.get(context);
+ if (contextualSearchStateManager.getLPNHDurationMillis().isPresent()) {
+ mLongPressTimeout =
+ contextualSearchStateManager.getLPNHDurationMillis().get().intValue();
} else {
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 995635f..dd721e1 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -44,20 +44,16 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.dagger.ApplicationContext;
-import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.DeviceGridState;
-import com.android.launcher3.util.DaggerSingletonObject;
-import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.ExecutorUtil;
+import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
-import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -65,12 +61,9 @@
import java.io.IOException;
import java.util.Optional;
-import javax.inject.Inject;
-
/**
* Utility class to log launcher settings changes
*/
-@LauncherAppSingleton
public class SettingsChangeLogger implements
DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener,
SafeCloseable {
@@ -78,8 +71,8 @@
/**
* Singleton instance
*/
- public static DaggerSingletonObject<SettingsChangeLogger> INSTANCE =
- new DaggerSingletonObject<>(QuickstepBaseAppComponent::getSettingsChangeLogger);
+ public static MainThreadInitializedObject<SettingsChangeLogger> INSTANCE =
+ new MainThreadInitializedObject<>(SettingsChangeLogger::new);
private static final String TAG = "SettingsChangeLogger";
private static final String BOOLEAN_PREF = "SwitchPreference";
@@ -92,31 +85,26 @@
private StatsLogManager.LauncherEvent mNotificationDotsEvent;
private StatsLogManager.LauncherEvent mHomeScreenSuggestionEvent;
- @Inject
- SettingsChangeLogger(@ApplicationContext Context context, DaggerSingletonTracker tracker) {
- this(context, StatsLogManager.newInstance(context), tracker);
+ SettingsChangeLogger(@ApplicationContext Context context) {
+ this(context, StatsLogManager.newInstance(context));
}
@VisibleForTesting
- SettingsChangeLogger(Context context, StatsLogManager statsLogManager,
- DaggerSingletonTracker tracker) {
+ SettingsChangeLogger(Context context, StatsLogManager statsLogManager) {
mContext = context;
mStatsLogManager = statsLogManager;
mLoggablePrefs = loadPrefKeys(context);
- ExecutorUtil.executeSyncOnMainOrFail(() -> {
- DisplayController.INSTANCE.get(context).addChangeListener(this);
- mNavMode = DisplayController.getNavigationMode(context);
+ DisplayController.INSTANCE.get(context).addChangeListener(this);
+ mNavMode = DisplayController.getNavigationMode(context);
- getPrefs(context).registerOnSharedPreferenceChangeListener(this);
- getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
+ getPrefs(context).registerOnSharedPreferenceChangeListener(this);
+ getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
- SettingsCache settingsCache = SettingsCache.INSTANCE.get(context);
- settingsCache.register(NOTIFICATION_BADGING_URI,
- this::onNotificationDotsChanged);
- onNotificationDotsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
- tracker.addCloseable(this);
- });
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(context);
+ settingsCache.register(NOTIFICATION_BADGING_URI,
+ this::onNotificationDotsChanged);
+ onNotificationDotsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
}
private static ArrayMap<String, LoggablePref> loadPrefKeys(Context context) {
@@ -223,8 +211,6 @@
public void close() {
getPrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
getDevicePrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
- SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
- settingsCache.unregister(NOTIFICATION_BADGING_URI, this::onNotificationDotsChanged);
}
@VisibleForTesting
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
deleted file mode 100644
index 7acb28d..0000000
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.launcher3.util.SafeCloseable;
-
-import java.io.PrintWriter;
-import java.util.Optional;
-
-/** Class to manage Assistant states. */
-public class AssistStateManager implements ResourceBasedOverride, SafeCloseable {
-
- public static final MainThreadInitializedObject<AssistStateManager> INSTANCE =
- forOverride(AssistStateManager.class, R.string.assist_state_manager_class);
-
- public AssistStateManager() {}
-
- /** Return {@code true} if the Settings toggle is enabled. */
- public boolean isSettingsAllEntrypointsEnabled() {
- return false;
- }
-
- /** Whether search supports showing on the lockscreen. */
- public boolean supportsShowWhenLocked() {
- return false;
- }
-
- /** Whether ContextualSearchService invocation path is available. */
- public boolean isContextualSearchServiceAvailable() {
- return false;
- }
-
- /** Get the Launcher overridden long press nav handle duration to trigger Assistant. */
- public Optional<Long> getLPNHDurationMillis() {
- return Optional.empty();
- }
-
- /**
- * Get the Launcher overridden long press nav handle touch slop multiplier to trigger Assistant.
- */
- public Optional<Float> getLPNHCustomSlopMultiplier() {
- return Optional.empty();
- }
-
- /** Get the Launcher overridden long press home duration to trigger Assistant. */
- public Optional<Long> getLPHDurationMillis() {
- return Optional.empty();
- }
-
- /** Get the Launcher overridden long press home touch slop multiplier to trigger Assistant. */
- public Optional<Float> getLPHCustomSlopMultiplier() {
- return Optional.empty();
- }
-
- /** Get the long press duration data source. */
- public int getDurationDataSource() {
- return 0;
- }
-
- /** Get the long press touch slop multiplier data source. */
- public int getSlopDataSource() {
- return 0;
- }
-
- /** Get the haptic bit overridden by AGSA. */
- public Optional<Boolean> getShouldPlayHapticOverride() {
- return Optional.empty();
- }
-
- /** Dump states. */
- public void dump(String prefix, PrintWriter writer) {}
-
- @Override
- public void close() {}
-}
diff --git a/quickstep/src/com/android/quickstep/util/AssistUtils.java b/quickstep/src/com/android/quickstep/util/AssistUtils.java
deleted file mode 100644
index 11b6ea7..0000000
--- a/quickstep/src/com/android/quickstep/util/AssistUtils.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import android.content.Context;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/** Utilities to work with Assistant functionality. */
-public class AssistUtils implements ResourceBasedOverride {
-
- public AssistUtils() {}
-
- /** Creates AssistUtils as specified by overrides */
- public static AssistUtils newInstance(Context context) {
- return Overrides.getObject(AssistUtils.class, context, R.string.assist_utils_class);
- }
-
- /** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */
- public int[] getSysUiAssistOverrideInvocationTypes() {
- return new int[0];
- }
-
- /**
- * @return {@code true} if the override was handled, i.e. an assist surface was shown or the
- * request should be ignored. {@code false} means the caller should start assist another way.
- */
- public boolean tryStartAssistOverride(int invocationType) {
- return false;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchHapticManager.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchHapticManager.kt
new file mode 100644
index 0000000..286b77a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchHapticManager.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.content.Context
+import android.os.VibrationEffect
+import android.os.VibrationEffect.Composition
+import android.os.Vibrator
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.util.MainThreadInitializedObject
+import com.android.launcher3.util.SafeCloseable
+import com.android.launcher3.util.VibratorWrapper
+import com.android.quickstep.DeviceConfigWrapper.Companion.get
+import kotlin.math.pow
+
+/** Manages haptics relating to Contextual Search invocations. */
+class ContextualSearchHapticManager
+internal constructor(@ApplicationContext private val context: Context) : SafeCloseable {
+
+ private var searchEffect = createSearchEffect()
+ private var contextualSearchStateManager = ContextualSearchStateManager.INSTANCE[context]
+
+ private fun createSearchEffect() =
+ if (
+ context
+ .getSystemService(Vibrator::class.java)!!
+ .areAllPrimitivesSupported(Composition.PRIMITIVE_TICK)
+ ) {
+ VibrationEffect.startComposition()
+ .addPrimitive(Composition.PRIMITIVE_TICK, 1f)
+ .compose()
+ } else {
+ // fallback for devices without composition support
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK)
+ }
+
+ /** Indicates that search has been invoked. */
+ fun vibrateForSearch() {
+ searchEffect.let { VibratorWrapper.INSTANCE[context].vibrate(it) }
+ }
+
+ /** Indicates that search will be invoked if the current gesture is maintained. */
+ fun vibrateForSearchHint() {
+ val navbarConfig = get()
+ // Whether we should play the hint (ramp up) haptic
+ val shouldVibrate: Boolean =
+ if (
+ context
+ .getSystemService(Vibrator::class.java)!!
+ .areAllPrimitivesSupported(Composition.PRIMITIVE_LOW_TICK)
+ ) {
+ if (contextualSearchStateManager.shouldPlayHapticOverride.isPresent) {
+ contextualSearchStateManager.shouldPlayHapticOverride.get()
+ } else {
+ navbarConfig.enableSearchHapticHint
+ }
+ } else {
+ false
+ }
+
+ if (shouldVibrate) {
+ val startScale = navbarConfig.lpnhHapticHintStartScalePercent / 100f
+ val endScale = navbarConfig.lpnhHapticHintEndScalePercent / 100f
+ val scaleExponent = navbarConfig.lpnhHapticHintScaleExponent
+ val iterations = navbarConfig.lpnhHapticHintIterations
+ val delayMs = navbarConfig.lpnhHapticHintDelay
+ val composition = VibrationEffect.startComposition()
+ for (i in 0 until iterations) {
+ val t = i / (iterations - 1f)
+ val scale =
+ ((1 - t) * startScale + t * endScale)
+ .toDouble()
+ .pow(scaleExponent.toDouble())
+ .toFloat()
+ if (i == 0) {
+ // Adds a delay before the ramp starts
+ composition.addPrimitive(Composition.PRIMITIVE_LOW_TICK, scale, delayMs)
+ } else {
+ composition.addPrimitive(Composition.PRIMITIVE_LOW_TICK, scale)
+ }
+ }
+ VibratorWrapper.INSTANCE[context].vibrate(composition.compose())
+ }
+ }
+
+ override fun close() {}
+
+ companion object {
+ @JvmField val INSTANCE = MainThreadInitializedObject { ContextualSearchHapticManager(it) }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
new file mode 100644
index 0000000..bd454c0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.app.contextualsearch.ContextualSearchManager
+import android.app.contextualsearch.ContextualSearchManager.ENTRYPOINT_LONG_PRESS_HOME
+import android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH
+import android.content.Context
+import android.util.Log
+import com.android.internal.app.AssistUtils
+import com.android.launcher3.R
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_KEYGUARD
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_NOTIFICATION_SHADE
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_ATTEMPTED_SPLITSCREEN
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_FAILED_NOT_AVAILABLE
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_FAILED_SETTING_DISABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_HOME
+import com.android.launcher3.util.ResourceBasedOverride
+import com.android.quickstep.DeviceConfigWrapper
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.TopTaskTracker
+import com.android.systemui.shared.system.QuickStepContract
+
+/** Handles invocations and checks for Contextual Search. */
+open class ContextualSearchInvoker
+internal constructor(
+ protected val context: Context,
+ private val contextualSearchStateManager: ContextualSearchStateManager,
+ private val topTaskTracker: TopTaskTracker,
+ private val systemUiProxy: SystemUiProxy,
+ protected val statsLogManager: StatsLogManager,
+ private val contextualSearchHapticManager: ContextualSearchHapticManager,
+ private val contextualSearchManager: ContextualSearchManager?,
+) : ResourceBasedOverride {
+ constructor(
+ context: Context
+ ) : this(
+ context,
+ ContextualSearchStateManager.INSTANCE[context],
+ TopTaskTracker.INSTANCE[context],
+ SystemUiProxy.INSTANCE[context],
+ StatsLogManager.newInstance(context),
+ ContextualSearchHapticManager.INSTANCE[context],
+ context.getSystemService(ContextualSearchManager::class.java),
+ )
+
+ /** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */
+ open fun getSysUiAssistOverrideInvocationTypes(): IntArray {
+ val overrideInvocationTypes = com.android.launcher3.util.IntArray()
+ if (context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) {
+ overrideInvocationTypes.add(AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)
+ }
+ return overrideInvocationTypes.toArray()
+ }
+
+ /**
+ * @return `true` if the override was handled, i.e. an assist surface was shown or the request
+ * should be ignored. `false` means the caller should start assist another way.
+ */
+ fun tryStartAssistOverride(invocationType: Int): Boolean {
+ if (invocationType == AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS) {
+ if (!context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) {
+ // When Contextual Search is disabled, fall back to Assistant.
+ return false
+ }
+
+ val success = show(ENTRYPOINT_LONG_PRESS_HOME)
+ if (success) {
+ val runningPackage =
+ TopTaskTracker.INSTANCE[context].getCachedTopTask(
+ /* filterOnlyVisibleRecents */ true
+ )
+ .getPackageName()
+ statsLogManager
+ .logger()
+ .withPackageName(runningPackage)
+ .log(LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_HOME)
+ }
+
+ // Regardless of success, do not fall back to other assistant.
+ return true
+ }
+ return false
+ }
+
+ /**
+ * Invoke Contextual Search via ContextualSearchService if availability checks are successful
+ *
+ * @param entryPoint one of the ENTRY_POINT_* constants defined in this class
+ * @return true if invocation was successful, false otherwise
+ */
+ fun show(entryPoint: Int): Boolean {
+ return if (!runContextualSearchInvocationChecksAndLogFailures()) false
+ else invokeContextualSearchUnchecked(entryPoint)
+ }
+
+ /**
+ * Run availability checks and log errors to WW. If successful the caller is expected to call
+ * {@link invokeContextualSearchUnchecked}
+ *
+ * @return true if availability checks were successful, false otherwise.
+ */
+ fun runContextualSearchInvocationChecksAndLogFailures(): Boolean {
+ if (
+ contextualSearchManager == null ||
+ !context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)
+ ) {
+ Log.i(TAG, "Contextual Search invocation failed: no ContextualSearchManager")
+ statsLogManager.logger().log(LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR)
+ return false
+ }
+ if (!contextualSearchStateManager.isContextualSearchSettingEnabled) {
+ Log.i(TAG, "Contextual Search invocation failed: setting disabled")
+ statsLogManager.logger().log(LAUNCHER_LAUNCH_OMNI_FAILED_SETTING_DISABLED)
+ return false
+ }
+ if (isNotificationShadeShowing()) {
+ Log.i(TAG, "Contextual Search invocation failed: notification shade")
+ statsLogManager.logger().log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_NOTIFICATION_SHADE)
+ return false
+ }
+ if (isKeyguardShowing()) {
+ Log.i(TAG, "Contextual Search invocation attempted: keyguard")
+ statsLogManager.logger().log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_KEYGUARD)
+ if (!contextualSearchStateManager.isInvocationAllowedOnKeyguard) {
+ Log.i(TAG, "Contextual Search invocation failed: keyguard not allowed")
+ return false
+ } else if (!contextualSearchStateManager.supportsShowWhenLocked()) {
+ Log.i(TAG, "Contextual Search invocation failed: AGA doesn't support keyguard")
+ return false
+ }
+ }
+ if (isInSplitscreen()) {
+ Log.i(TAG, "Contextual Search invocation attempted: splitscreen")
+ statsLogManager.logger().log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_SPLITSCREEN)
+ if (!contextualSearchStateManager.isInvocationAllowedInSplitscreen) {
+ Log.i(TAG, "Contextual Search invocation failed: splitscreen not allowed")
+ return false
+ }
+ }
+ if (!contextualSearchStateManager.isContextualSearchIntentAvailable) {
+ Log.i(TAG, "Contextual Search invocation failed: no matching CSS intent filter")
+ statsLogManager.logger().log(LAUNCHER_LAUNCH_OMNI_FAILED_NOT_AVAILABLE)
+ return false
+ }
+
+ return true
+ }
+
+ /**
+ * Invoke Contextual Search via ContextualSearchService and do haptic
+ *
+ * @param entryPoint Entry point identifier, passed to ContextualSearchService.
+ * @return true if invocation was successful, false otherwise
+ */
+ fun invokeContextualSearchUncheckedWithHaptic(entryPoint: Int): Boolean {
+ return invokeContextualSearchUnchecked(entryPoint, withHaptic = true)
+ }
+
+ private fun invokeContextualSearchUnchecked(
+ entryPoint: Int,
+ withHaptic: Boolean = false,
+ ): Boolean {
+ if (withHaptic && DeviceConfigWrapper.get().enableSearchHapticCommit) {
+ contextualSearchHapticManager.vibrateForSearch()
+ }
+ if (contextualSearchManager == null) {
+ return false
+ }
+ contextualSearchManager.startContextualSearch(entryPoint)
+ return true
+ }
+
+ private fun isInSplitscreen(): Boolean {
+ return topTaskTracker.getRunningSplitTaskIds().isNotEmpty()
+ }
+
+ private fun isNotificationShadeShowing(): Boolean {
+ return systemUiProxy.lastSystemUiStateFlags and SHADE_EXPANDED_SYSUI_FLAGS != 0L
+ }
+
+ private fun isKeyguardShowing(): Boolean {
+ return systemUiProxy.lastSystemUiStateFlags and KEYGUARD_SHOWING_SYSUI_FLAGS != 0L
+ }
+
+ companion object {
+ private const val TAG = "ContextualSearchInvoker"
+ const val SHADE_EXPANDED_SYSUI_FLAGS =
+ QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED or
+ QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
+ const val KEYGUARD_SHOWING_SYSUI_FLAGS =
+ (QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING or
+ QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING or
+ QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED)
+
+ @JvmStatic
+ fun newInstance(context: Context): ContextualSearchInvoker {
+ return ResourceBasedOverride.Overrides.getObject(
+ ContextualSearchInvoker::class.java,
+ context,
+ R.string.contextual_search_invoker_class,
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
new file mode 100644
index 0000000..083f192
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static android.app.contextualsearch.ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH;
+import static android.app.contextualsearch.ContextualSearchManager.ENTRYPOINT_SYSTEM_ACTION;
+import static android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_SEARCH_SCREEN;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.util.EventLogArray;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.quickstep.DeviceConfigWrapper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TopTaskTracker;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+/** Long-lived class to manage Contextual Search states like the user setting and availability. */
+public class ContextualSearchStateManager implements ResourceBasedOverride, SafeCloseable {
+
+ public static final MainThreadInitializedObject<ContextualSearchStateManager> INSTANCE =
+ forOverride(ContextualSearchStateManager.class,
+ R.string.contextual_search_state_manager_class);
+
+ private static final String TAG = "ContextualSearchStMgr";
+ private static final int MAX_DEBUG_EVENT_SIZE = 20;
+ private static final Uri SEARCH_ALL_ENTRYPOINTS_ENABLED_URI =
+ Settings.Secure.getUriFor(Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED);
+
+ private final Runnable mSysUiStateChangeListener = this::updateOverridesToSysUi;
+ private final SimpleBroadcastReceiver mContextualSearchPackageReceiver =
+ new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, (unused) -> requestUpdateProperties());
+ private final SettingsCache.OnChangeListener mContextualSearchSettingChangedListener =
+ this::onContextualSearchSettingChanged;
+ protected final EventLogArray mEventLogArray = new EventLogArray(TAG, MAX_DEBUG_EVENT_SIZE);
+
+ @Nullable private SettingsCache mSettingsCache;
+ // Cached value whether the ContextualSearch intent filter matched any enabled components.
+ private boolean mIsContextualSearchIntentAvailable;
+ private boolean mIsContextualSearchSettingEnabled;
+
+ protected Context mContext;
+ protected String mContextualSearchPackage;
+
+ public ContextualSearchStateManager() {}
+
+ public ContextualSearchStateManager(Context context) {
+ mContext = context;
+ mContextualSearchPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_defaultContextualSearchPackageName);
+
+ if (areAllContextualSearchFlagsDisabled()
+ || !context.getPackageManager().hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) {
+ // If we had previously registered a SystemAction which is no longer valid, we need to
+ // unregister it here.
+ unregisterSearchScreenSystemAction();
+ // Don't listen for stuff we aren't gonna use.
+ return;
+ }
+
+ requestUpdateProperties();
+ registerSearchScreenSystemAction();
+ mContextualSearchPackageReceiver.registerPkgActions(
+ context, mContextualSearchPackage, Intent.ACTION_PACKAGE_ADDED,
+ Intent.ACTION_PACKAGE_CHANGED, Intent.ACTION_PACKAGE_REMOVED);
+
+ mSettingsCache = SettingsCache.INSTANCE.get(context);
+ mSettingsCache.register(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI,
+ mContextualSearchSettingChangedListener);
+ onContextualSearchSettingChanged(
+ mSettingsCache.getValue(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI));
+ SystemUiProxy.INSTANCE.get(mContext).addOnStateChangeListener(mSysUiStateChangeListener);
+ }
+
+ /** Return {@code true} if the Settings toggle is enabled. */
+ public final boolean isContextualSearchSettingEnabled() {
+ return mIsContextualSearchSettingEnabled;
+ }
+
+ private void onContextualSearchSettingChanged(boolean isEnabled) {
+ mIsContextualSearchSettingEnabled = isEnabled;
+ }
+
+ /** Whether search supports showing on the lockscreen. */
+ protected boolean supportsShowWhenLocked() {
+ return false;
+ }
+
+ /** Whether ContextualSearchService invocation path is available. */
+ @VisibleForTesting
+ protected final boolean isContextualSearchIntentAvailable() {
+ return mIsContextualSearchIntentAvailable;
+ }
+
+ /** Get the Launcher overridden long press nav handle duration to trigger Assistant. */
+ public Optional<Long> getLPNHDurationMillis() {
+ return Optional.empty();
+ }
+
+ /**
+ * Get the Launcher overridden long press nav handle touch slop multiplier to trigger Assistant.
+ */
+ public Optional<Float> getLPNHCustomSlopMultiplier() {
+ return Optional.empty();
+ }
+
+ /** Get the Launcher overridden long press home duration to trigger Assistant. */
+ public Optional<Long> getLPHDurationMillis() {
+ return Optional.empty();
+ }
+
+ /** Get the Launcher overridden long press home touch slop multiplier to trigger Assistant. */
+ public Optional<Float> getLPHCustomSlopMultiplier() {
+ return Optional.empty();
+ }
+
+ /** Get the long press duration data source. */
+ public int getDurationDataSource() {
+ return 0;
+ }
+
+ /** Get the long press touch slop multiplier data source. */
+ public int getSlopDataSource() {
+ return 0;
+ }
+
+ /**
+ * Get the User group based on the behavior to trigger Assistant.
+ */
+ public Optional<Integer> getLPUserGroup() {
+ return Optional.empty();
+ }
+
+ /** Get the haptic bit overridden by AGSA. */
+ public Optional<Boolean> getShouldPlayHapticOverride() {
+ return Optional.empty();
+ }
+
+ protected boolean isInvocationAllowedOnKeyguard() {
+ return false;
+ }
+
+ protected boolean isInvocationAllowedInSplitscreen() {
+ return true;
+ }
+
+ @CallSuper
+ protected boolean areAllContextualSearchFlagsDisabled() {
+ return !DeviceConfigWrapper.get().getEnableLongPressNavHandle();
+ }
+
+ @CallSuper
+ protected void requestUpdateProperties() {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ // Check that Contextual Search intent filters are enabled.
+ Intent csIntent = new Intent(ACTION_LAUNCH_CONTEXTUAL_SEARCH).setPackage(
+ mContextualSearchPackage);
+ mIsContextualSearchIntentAvailable =
+ !mContext.getPackageManager().queryIntentActivities(csIntent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE).isEmpty();
+
+ addEventLog("Updated isContextualSearchIntentAvailable",
+ mIsContextualSearchIntentAvailable);
+ });
+ }
+
+ protected final void updateOverridesToSysUi() {
+ // LPH commit haptic is always enabled
+ SystemUiProxy.INSTANCE.get(mContext).setOverrideHomeButtonLongPress(
+ getLPHDurationMillis().orElse(0L), getLPHCustomSlopMultiplier().orElse(0f), true);
+ Log.i(TAG, "Sent LPH override to sysui: " + getLPHDurationMillis().orElse(0L) + ";"
+ + getLPHCustomSlopMultiplier().orElse(0f));
+ }
+
+ private void registerSearchScreenSystemAction() {
+ PendingIntent searchScreenPendingIntent = new PendingIntent(new IIntentSender.Stub() {
+ @Override
+ public void send(int i, Intent intent, String s, IBinder iBinder,
+ IIntentReceiver iIntentReceiver, String s1, Bundle bundle)
+ throws RemoteException {
+ // Delayed slightly to minimize chance of capturing the System Actions dialog.
+ UI_HELPER_EXECUTOR.getHandler().postDelayed(
+ () -> {
+ boolean contextualSearchInvoked =
+ ContextualSearchInvoker.newInstance(mContext).show(
+ ENTRYPOINT_SYSTEM_ACTION);
+ if (contextualSearchInvoked) {
+ String runningPackage =
+ TopTaskTracker.INSTANCE.get(mContext).getCachedTopTask(
+ /* filterOnlyVisibleRecents */
+ true).getPackageName();
+ StatsLogManager.newInstance(mContext).logger()
+ .withPackageName(runningPackage)
+ .log(LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION);
+ }
+ }, 200);
+ }
+ });
+
+ mContext.getSystemService(AccessibilityManager.class).registerSystemAction(new RemoteAction(
+ Icon.createWithResource(mContext, R.drawable.ic_allapps_search),
+ mContext.getString(R.string.search_gesture_feature_title),
+ mContext.getString(R.string.search_gesture_feature_title),
+ searchScreenPendingIntent),
+ SYSTEM_ACTION_ID_SEARCH_SCREEN);
+ }
+
+ private void unregisterSearchScreenSystemAction() {
+ mContext.getSystemService(AccessibilityManager.class).unregisterSystemAction(
+ SYSTEM_ACTION_ID_SEARCH_SCREEN);
+ }
+
+ /** Dump states. */
+ public final void dump(String prefix, PrintWriter writer) {
+ synchronized (mEventLogArray) {
+ mEventLogArray.dump(prefix, writer);
+ }
+ }
+
+ @Override
+ public void close() {
+ mContextualSearchPackageReceiver.unregisterReceiverSafely(mContext);
+ unregisterSearchScreenSystemAction();
+
+ if (mSettingsCache != null) {
+ mSettingsCache.unregister(SEARCH_ALL_ENTRYPOINTS_ENABLED_URI,
+ mContextualSearchSettingChangedListener);
+ }
+ SystemUiProxy.INSTANCE.get(mContext).removeOnStateChangeListener(mSysUiStateChangeListener);
+ }
+
+ protected final void addEventLog(String event) {
+ synchronized (mEventLogArray) {
+ mEventLogArray.addLog(event);
+ }
+ }
+
+ protected final void addEventLog(String event, boolean extras) {
+ synchronized (mEventLogArray) {
+ mEventLogArray.addLog(event, extras);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
index a727aa2..fc4fc4d 100644
--- a/quickstep/src/com/android/quickstep/util/DesktopTask.java
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -50,6 +50,11 @@
@Override
public boolean hasMultipleTasks() {
+ return tasks.size() > 1;
+ }
+
+ @Override
+ public boolean supportsMultipleTasks() {
return true;
}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index fba08a9..7aeeb2f 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -66,6 +66,13 @@
}
/**
+ * Returns whether this task supports multiple tasks or not.
+ */
+ public boolean supportsMultipleTasks() {
+ return taskViewType == TaskViewType.GROUPED;
+ }
+
+ /**
* Returns a List of all the Tasks in this GroupTask
*/
public List<Task> getTasks() {
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 511c989..ea582c4 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -900,7 +900,7 @@
SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
.startRecentsActivity(
mOverviewComponentObserver.getOverviewIntent(), options,
- callbacks);
+ callbacks, false /* useSyntheticRecentsTransition */);
});
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 4c6e4ff..744c08c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -99,7 +99,8 @@
options.setTransientLaunch();
SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
.startRecentsActivity(mOverviewComponentObserver.getOverviewIntent(),
- ActivityOptions.makeBasic(), callbacks);
+ ActivityOptions.makeBasic(), callbacks,
+ false /* useSyntheticRecentsTransition */);
});
});
}
diff --git a/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt b/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt
index 5f4388c..1ff05da 100644
--- a/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/SystemUiFlagUtils.kt
@@ -47,6 +47,22 @@
!hasAnyFlag(flags, QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY)
}
+ /**
+ * Taskbar is hidden whenever the device is dreaming. The dreaming state includes the
+ * interactive dreams, AoD, screen off. Since the SYSUI_STATE_DEVICE_DREAMING only kicks in when
+ * the device is asleep, the second condition extends ensures that the transition from and to
+ * the WAKEFULNESS_ASLEEP state also hide the taskbar, and improves the taskbar hide/reveal
+ * animation timings. The Taskbar can show when dreaming if the glanceable hub is showing on
+ * top.
+ */
+ @JvmStatic
+ fun isTaskbarHidden(@SystemUiStateFlags flags: Long): Boolean {
+ return ((hasAnyFlag(flags, QuickStepContract.SYSUI_STATE_DEVICE_DREAMING) &&
+ !hasAnyFlag(flags, QuickStepContract.SYSUI_STATE_COMMUNAL_HUB_SHOWING)) ||
+ (flags and QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK) !=
+ QuickStepContract.WAKEFULNESS_AWAKE)
+ }
+
private fun hasAnyFlag(@SystemUiStateFlags flags: Long, flagMask: Long): Boolean {
return (flags and flagMask) != 0L
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7554c44..5458ba1 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1885,19 +1885,22 @@
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean isRemovalNeeded = stagedTaskIdToBeRemoved != INVALID_TASK_ID
+ boolean containsStagedTask = stagedTaskIdToBeRemoved != INVALID_TASK_ID
&& groupTask.containsTask(stagedTaskIdToBeRemoved);
+ boolean shouldSkipGroupTask = containsStagedTask && !groupTask.hasMultipleTasks();
- if (isRemovalNeeded && !groupTask.hasMultipleTasks()) {
- // If the task we need to remove is not part of a pair, avoiding creating the
- // TaskView.
+ if ((isSplitSelectionActive() && groupTask.taskViewType == TaskViewType.DESKTOP)
+ || shouldSkipGroupTask) {
+ // To avoid these tasks from being chosen as the app pair, the creation of a
+ // TaskView is bypassed. The staged task is already selected for the app pair,
+ // and the Desktop task should be hidden when selecting a pair.
continue;
}
// If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
// to be a temporary container for the remaining task.
TaskView taskView = getTaskViewFromPool(
- isRemovalNeeded ? TaskViewType.SINGLE : groupTask.taskViewType);
+ containsStagedTask ? TaskViewType.SINGLE : groupTask.taskViewType);
if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
@@ -3807,7 +3810,7 @@
}
} else if (!showAsGrid || (enableLargeDesktopWindowingTile()
&& dismissedTaskView.isLargeTile()
- && nextFocusedTaskView == null)) {
+ && nextFocusedTaskView == null && !dismissingForSplitSelection)) {
int offset = getOffsetToDismissedTask(scrollDiffPerPage, dismissedIndex, taskCount);
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
@@ -5572,15 +5575,13 @@
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
.addOverviewToAppAnim(mPendingAnimation, interpolator));
mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
- if (taskView instanceof DesktopTaskView && mRemoteTargetHandles != null) {
- mPendingAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
- }
- });
- }
+ mPendingAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
+ }
+ });
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
if (taskView instanceof GroupedTaskView && hasAllValidTaskIds(taskView.getTaskIds())
@@ -5612,6 +5613,13 @@
protected Unit onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
+ } else {
+ // If launch animation didn't complete i.e. user dragged live tile down and then
+ // back up and returned to Overview, then we need to ensure we reset the
+ // view to draw below recents so that it can't be interacted with.
+ runActionOnRemoteHandles(remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ redrawLiveTile();
}
return Unit.INSTANCE;
}
@@ -6673,6 +6681,26 @@
successCallback.run();
}
+ /**
+ * Move the provided task into external display and invoke {@code successCallback} if succeeded.
+ */
+ public void moveTaskToExternalDisplay(TaskContainer taskContainer, Runnable successCallback) {
+ if (!DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ return;
+ }
+ switchToScreenshot(() -> finishRecentsAnimation(/* toRecents= */true, /* shouldPip= */false,
+ () -> moveTaskToDesktopInternal(taskContainer, successCallback)));
+ }
+
+ private void moveTaskToDesktopInternal(TaskContainer taskContainer, Runnable successCallback) {
+ if (mDesktopRecentsTransitionController == null) {
+ return;
+ }
+ mDesktopRecentsTransitionController.moveToExternalDisplay(taskContainer.getTask().key.id);
+ successCallback.run();
+ }
+
+
// Logs when the orientation of Overview changes. We log both real and fake orientation changes.
private void logOrientationChanged() {
// Only log when Overview is showing.
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
index a57fb70..6e2f74a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
@@ -16,10 +16,34 @@
package com.android.launcher3.taskbar
+import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.ConstantItem
+import com.android.launcher3.LauncherPrefs
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
object TaskbarControllerTestUtil {
inline fun runOnMainSync(crossinline runTest: () -> Unit) {
getInstrumentation().runOnMainSync { runTest() }
}
+
+ /** Returns a property to read/write the value of a [ConstantItem]. */
+ fun <T : Any> ConstantItem<T>.asProperty(context: Context): ReadWriteProperty<Any?, T> {
+ return TaskbarItemProperty(context, this)
+ }
+
+ private class TaskbarItemProperty<T : Any>(
+ private val context: Context,
+ private val item: ConstantItem<T>,
+ ) : ReadWriteProperty<Any?, T> {
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T {
+ return LauncherPrefs.get(context).get(item)
+ }
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
+ runOnMainSync { LauncherPrefs.get(context).put(item, value) }
+ }
+ }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt
index e575efd..e60717b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt
@@ -14,24 +14,17 @@
* limitations under the License.
*/
-package com.android.launcher3.taskbar.test
+package com.android.launcher3.taskbar
import android.util.Log
import com.android.launcher3.Utilities
-import com.android.launcher3.taskbar.TOOLTIP_STEP_FEATURES
-import com.android.launcher3.taskbar.TOOLTIP_STEP_NONE
-import com.android.launcher3.taskbar.TOOLTIP_STEP_PINNING
-import com.android.launcher3.taskbar.TOOLTIP_STEP_SWIPE
-import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.asProperty
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
-import com.android.launcher3.taskbar.TaskbarEduTooltipController
import com.android.launcher3.taskbar.rules.TaskbarModeRule
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
-import com.android.launcher3.taskbar.rules.TaskbarPinningPreferenceRule
-import com.android.launcher3.taskbar.rules.TaskbarPreferenceRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
@@ -53,19 +46,9 @@
@get:Rule(order = 0) val context = TaskbarWindowSandboxContext.create()
- @get:Rule(order = 1)
- val tooltipStepPreferenceRule =
- TaskbarPreferenceRule(context, OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.prefItem)
+ @get:Rule(order = 1) val taskbarModeRule = TaskbarModeRule(context)
- @get:Rule(order = 2)
- val searchEduPreferenceRule =
- TaskbarPreferenceRule(context, OnboardingPrefs.TASKBAR_SEARCH_EDU_SEEN)
-
- @get:Rule(order = 3) val taskbarPinningPreferenceRule = TaskbarPinningPreferenceRule(context)
-
- @get:Rule(order = 4) val taskbarModeRule = TaskbarModeRule(context)
-
- @get:Rule(order = 5) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 2) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@InjectController lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
@@ -74,6 +57,9 @@
private val wasInTestHarness = Utilities.isRunningInTestHarness()
+ private var tooltipStep by OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.prefItem.asProperty(context)
+ private var searchEduSeen by OnboardingPrefs.TASKBAR_SEARCH_EDU_SEEN.asProperty(context)
+
@Before
fun setUp() {
Log.e("Taskbar", "TaskbarEduTooltipControllerTest test started")
@@ -91,7 +77,7 @@
@Test
@TaskbarMode(THREE_BUTTONS)
fun testMaybeShowSwipeEdu_whenTaskbarIsInThreeButtonMode_doesNotShowSwipeEdu() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ tooltipStep = TOOLTIP_STEP_SWIPE
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
@@ -101,7 +87,7 @@
@Test
@TaskbarMode(TRANSIENT)
fun testMaybeShowSwipeEdu_whenSwipeEduAlreadyShown_doesNotShowSwipeEdu() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_FEATURES
+ tooltipStep = TOOLTIP_STEP_FEATURES
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
@@ -111,7 +97,7 @@
@Test
@TaskbarMode(TRANSIENT)
fun testMaybeShowSwipeEdu_whenUserHasNotSeen_doesShowSwipeEdu() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ tooltipStep = TOOLTIP_STEP_SWIPE
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
@@ -121,7 +107,7 @@
@Test
@TaskbarMode(TRANSIENT)
fun testMaybeShowFeaturesEdu_whenFeatureEduAlreadyShown_doesNotShowFeatureEdu() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_NONE
+ tooltipStep = TOOLTIP_STEP_NONE
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
@@ -131,7 +117,7 @@
@Test
@TaskbarMode(TRANSIENT)
fun testMaybeShowFeaturesEdu_whenUserHasNotSeen_doesShowFeatureEdu() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_FEATURES
+ tooltipStep = TOOLTIP_STEP_FEATURES
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
@@ -141,7 +127,7 @@
@Test
@TaskbarMode(THREE_BUTTONS)
fun testMaybeShowPinningEdu_whenTaskbarIsInThreeButtonMode_doesNotShowPinningEdu() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_PINNING
+ tooltipStep = TOOLTIP_STEP_PINNING
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
@@ -152,7 +138,7 @@
@TaskbarMode(TRANSIENT)
fun testMaybeShowPinningEdu_whenUserHasNotSeen_doesShowPinningEdu() {
// Test standalone pinning edu, where user has seen taskbar edu before, but not pinning edu.
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_PINNING
+ tooltipStep = TOOLTIP_STEP_PINNING
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
@@ -162,21 +148,21 @@
@Test
@TaskbarMode(TRANSIENT)
fun testIsBeforeTooltipFeaturesStep_whenUserHasNotSeenFeatureEdu_shouldReturnTrue() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ tooltipStep = TOOLTIP_STEP_SWIPE
assertThat(taskbarEduTooltipController.isBeforeTooltipFeaturesStep).isTrue()
}
@Test
@TaskbarMode(TRANSIENT)
fun testIsBeforeTooltipFeaturesStep_whenUserHasSeenFeatureEdu_shouldReturnFalse() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_NONE
+ tooltipStep = TOOLTIP_STEP_NONE
assertThat(taskbarEduTooltipController.isBeforeTooltipFeaturesStep).isFalse()
}
@Test
@TaskbarMode(TRANSIENT)
fun testHide_whenTooltipIsOpen_shouldCloseTooltip() {
- tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ tooltipStep = TOOLTIP_STEP_SWIPE
assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
@@ -196,7 +182,7 @@
@Test
@TaskbarMode(PINNED)
fun testMaybeShowSearchEdu_whenTaskbarIsPinnedAndUserHasSeenSearchEdu_shouldNotShowSearchEdu() {
- searchEduPreferenceRule.value = true
+ searchEduSeen = true
assertThat(taskbarEduTooltipController.userHasSeenSearchEdu).isTrue()
runOnMainSync { taskbarEduTooltipController.hide() }
assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index 02d6218..253d921 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -39,7 +39,7 @@
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
-import com.android.quickstep.util.AssistUtils;
+import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.systemui.contextualeducation.GestureType;
import org.junit.Before;
@@ -64,7 +64,7 @@
@Mock
Handler mockHandler;
@Mock
- AssistUtils mockAssistUtils;
+ ContextualSearchInvoker mockContextualSearchInvoker;
@Mock
StatsLogManager mockStatsLogManager;
@Mock
@@ -109,7 +109,7 @@
mockSystemUiProxy,
mockContextualEduStatsManager,
mockHandler,
- mockAssistUtils);
+ mockContextualSearchInvoker);
}
@Test
@@ -166,40 +166,40 @@
@Test
public void testLongPressHome_enabled_withoutOverride() {
mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/);
- when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(false);
+ when(mockContextualSearchInvoker.tryStartAssistOverride(anyInt())).thenReturn(false);
mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
- verify(mockAssistUtils, times(1)).tryStartAssistOverride(anyInt());
+ verify(mockContextualSearchInvoker, times(1)).tryStartAssistOverride(anyInt());
verify(mockSystemUiProxy, times(1)).startAssistant(any());
}
@Test
public void testLongPressHome_enabled_withOverride() {
mNavButtonController.setAssistantLongPressEnabled(true /*assistantLongPressEnabled*/);
- when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(true);
+ when(mockContextualSearchInvoker.tryStartAssistOverride(anyInt())).thenReturn(true);
mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
- verify(mockAssistUtils, times(1)).tryStartAssistOverride(anyInt());
+ verify(mockContextualSearchInvoker, times(1)).tryStartAssistOverride(anyInt());
verify(mockSystemUiProxy, never()).startAssistant(any());
}
@Test
public void testLongPressHome_disabled_withoutOverride() {
mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/);
- when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(false);
+ when(mockContextualSearchInvoker.tryStartAssistOverride(anyInt())).thenReturn(false);
mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
- verify(mockAssistUtils, never()).tryStartAssistOverride(anyInt());
+ verify(mockContextualSearchInvoker, never()).tryStartAssistOverride(anyInt());
verify(mockSystemUiProxy, never()).startAssistant(any());
}
@Test
public void testLongPressHome_disabled_withOverride() {
mNavButtonController.setAssistantLongPressEnabled(false /*assistantLongPressEnabled*/);
- when(mockAssistUtils.tryStartAssistOverride(anyInt())).thenReturn(true);
+ when(mockContextualSearchInvoker.tryStartAssistOverride(anyInt())).thenReturn(true);
mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
- verify(mockAssistUtils, never()).tryStartAssistOverride(anyInt());
+ verify(mockContextualSearchInvoker, never()).tryStartAssistOverride(anyInt());
verify(mockSystemUiProxy, never()).startAssistant(any());
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
index de73ce7..71f4ef4 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
@@ -18,10 +18,13 @@
import android.animation.AnimatorTestRule
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
import com.android.launcher3.R
import com.android.launcher3.taskbar.StashedHandleViewController.ALPHA_INDEX_STASHED
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.asProperty
import com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP
import com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW
import com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE
@@ -42,7 +45,6 @@
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
-import com.android.launcher3.taskbar.rules.TaskbarPinningPreferenceRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode
@@ -63,11 +65,11 @@
@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@EmulatedDevices(["pixelTablet2023"])
class TaskbarStashControllerTest {
- @get:Rule(order = 0) val context = TaskbarWindowSandboxContext.create()
- @get:Rule(order = 1) val taskbarModeRule = TaskbarModeRule(context)
- @get:Rule(order = 2) val taskbarPinningPreferenceRule = TaskbarPinningPreferenceRule(context)
- @get:Rule(order = 3) val animatorTestRule = AnimatorTestRule(this)
- @get:Rule(order = 4) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+ @get:Rule(order = 1) val context = TaskbarWindowSandboxContext.create()
+ @get:Rule(order = 2) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 4) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 5) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@InjectController lateinit var stashController: TaskbarStashController
@InjectController lateinit var viewController: TaskbarViewController
@@ -121,10 +123,11 @@
@Test
fun testRecreateAsTransient_timeoutStarted() {
- taskbarPinningPreferenceRule.isPinned = true
+ var isPinned by TASKBAR_PINNING.asProperty(context)
+ isPinned = true
activityContext.controllers.sharedState?.taskbarWasPinned = true
- taskbarPinningPreferenceRule.isPinned = false
+ isPinned = false
assertThat(stashController.timeoutAlarm.alarmPending()).isTrue()
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt
index 516220a..3c0d9c6 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewControllerTest.kt
@@ -21,6 +21,7 @@
import com.android.launcher3.appprediction.AppsDividerView
import com.android.launcher3.appprediction.AppsDividerView.DividerType
import com.android.launcher3.appprediction.PredictionRowView
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.asProperty
import com.android.launcher3.taskbar.TaskbarStashController
import com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_APP_AUTO
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsControllerTest.Companion.TEST_PREDICTED_APPS
@@ -29,7 +30,6 @@
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
-import com.android.launcher3.taskbar.rules.TaskbarPreferenceRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
@@ -49,14 +49,12 @@
@get:Rule(order = 0) val context = TaskbarWindowSandboxContext.create()
@get:Rule(order = 1) val taskbarModeRule = TaskbarModeRule(context)
- @get:Rule(order = 2)
- val allAppsVisitedPreferenceRule =
- TaskbarPreferenceRule(context, ALL_APPS_VISITED_COUNT.prefItem)
- @get:Rule(order = 3) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 2) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@InjectController lateinit var overlayController: TaskbarOverlayController
@InjectController lateinit var stashController: TaskbarStashController
+ private var allAppsVisitedCount by ALL_APPS_VISITED_COUNT.prefItem.asProperty(context)
private val searchSessionController =
TestUtil.getOnUiThread { TaskbarSearchSessionController.newInstance(context) }
@@ -102,7 +100,7 @@
@Test
fun testShow_firstAllAppsVisit_hasAllAppsTextDivider() {
- allAppsVisitedPreferenceRule.value = 0
+ allAppsVisitedCount = 0
val viewController = createViewController()
getInstrumentation().runOnMainSync { viewController.show(false) }
@@ -120,7 +118,7 @@
@Test
fun testShow_maxAllAppsVisitedCount_hasLineDivider() {
- allAppsVisitedPreferenceRule.value = ALL_APPS_VISITED_COUNT.maxCount
+ allAppsVisitedCount = ALL_APPS_VISITED_COUNT.maxCount
val viewController = createViewController()
getInstrumentation().runOnMainSync { viewController.show(false) }
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
index fdafce0..3dd7689 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -22,12 +22,15 @@
import android.view.Gravity
import android.widget.FrameLayout
import android.widget.TextView
+import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,11 +39,15 @@
@RunWith(AndroidJUnit4::class)
class BubbleBarFlyoutControllerTest {
+ @get:Rule val animatorTestRule = AnimatorTestRule()
+
private lateinit var flyoutController: BubbleBarFlyoutController
private lateinit var flyoutContainer: FrameLayout
+ private lateinit var topBoundaryListener: FakeTopBoundaryListener
private val context = ApplicationProvider.getApplicationContext<Context>()
private val flyoutMessage = BubbleBarFlyoutMessage(icon = null, "sender name", "message")
private var onLeft = true
+ private var flyoutTy = 50f
@Before
fun setUp() {
@@ -50,53 +57,126 @@
override val isOnLeft
get() = onLeft
- override val targetTy = 50f
+ override val targetTy
+ get() = flyoutTy
+
override val distanceToCollapsedPosition = PointF(100f, 200f)
override val collapsedSize = 30f
override val collapsedColor = Color.BLUE
override val collapsedElevation = 1f
override val distanceToRevealTriangle = 50f
}
- flyoutController = BubbleBarFlyoutController(flyoutContainer, positioner)
+ topBoundaryListener = FakeTopBoundaryListener()
+ val flyoutScheduler = FlyoutScheduler { block -> block.invoke() }
+ flyoutController =
+ BubbleBarFlyoutController(
+ flyoutContainer,
+ positioner,
+ topBoundaryListener,
+ flyoutScheduler,
+ )
}
@Test
fun flyoutPosition_left() {
- flyoutController.setUpFlyout(flyoutMessage)
- assertThat(flyoutContainer.childCount).isEqualTo(1)
- val flyout = flyoutContainer.getChildAt(0)
- val lp = flyout.layoutParams as FrameLayout.LayoutParams
- assertThat(lp.gravity).isEqualTo(Gravity.BOTTOM or Gravity.LEFT)
- assertThat(flyout.translationY).isEqualTo(50f)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyout = flyoutContainer.getChildAt(0)
+ val lp = flyout.layoutParams as FrameLayout.LayoutParams
+ assertThat(lp.gravity).isEqualTo(Gravity.BOTTOM or Gravity.LEFT)
+ assertThat(flyout.translationY).isEqualTo(50f)
+ }
}
@Test
fun flyoutPosition_right() {
onLeft = false
- flyoutController.setUpFlyout(flyoutMessage)
- assertThat(flyoutContainer.childCount).isEqualTo(1)
- val flyout = flyoutContainer.getChildAt(0)
- val lp = flyout.layoutParams as FrameLayout.LayoutParams
- assertThat(lp.gravity).isEqualTo(Gravity.BOTTOM or Gravity.RIGHT)
- assertThat(flyout.translationY).isEqualTo(50f)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyout = flyoutContainer.getChildAt(0)
+ val lp = flyout.layoutParams as FrameLayout.LayoutParams
+ assertThat(lp.gravity).isEqualTo(Gravity.BOTTOM or Gravity.RIGHT)
+ assertThat(flyout.translationY).isEqualTo(50f)
+ }
}
@Test
fun flyoutMessage() {
- flyoutController.setUpFlyout(flyoutMessage)
- assertThat(flyoutContainer.childCount).isEqualTo(1)
- val flyout = flyoutContainer.getChildAt(0)
- val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_title)
- assertThat(sender.text).isEqualTo("sender name")
- val message = flyout.findViewById<TextView>(R.id.bubble_flyout_text)
- assertThat(message.text).isEqualTo("message")
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ val flyout = flyoutContainer.getChildAt(0)
+ val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_title)
+ assertThat(sender.text).isEqualTo("sender name")
+ val message = flyout.findViewById<TextView>(R.id.bubble_flyout_text)
+ assertThat(message.text).isEqualTo("message")
+ }
}
@Test
fun hideFlyout_removedFromContainer() {
- flyoutController.setUpFlyout(flyoutMessage)
- assertThat(flyoutContainer.childCount).isEqualTo(1)
- flyoutController.hideFlyout()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ flyoutController.hideFlyout {}
+ animatorTestRule.advanceTimeBy(300)
+ }
assertThat(flyoutContainer.childCount).isEqualTo(0)
}
+
+ @Test
+ fun showFlyout_extendsTopBoundary() {
+ // set negative translation for the flyout so that it will request to extend the top
+ // boundary
+ flyoutTy = -50f
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ animatorTestRule.advanceTimeBy(300)
+ }
+ assertThat(topBoundaryListener.topBoundaryExtendedSpace).isEqualTo(50)
+ }
+
+ @Test
+ fun showFlyout_withinBoundary() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ animatorTestRule.advanceTimeBy(300)
+ }
+ assertThat(topBoundaryListener.topBoundaryExtendedSpace).isEqualTo(0)
+ }
+
+ @Test
+ fun hideFlyout_resetsTopBoundary() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpFlyout(flyoutMessage)
+ assertThat(flyoutContainer.childCount).isEqualTo(1)
+ flyoutController.hideFlyout {}
+ animatorTestRule.advanceTimeBy(300)
+ }
+ assertThat(topBoundaryListener.topBoundaryReset).isTrue()
+ }
+
+ class FakeTopBoundaryListener : BubbleBarFlyoutController.TopBoundaryListener {
+
+ var topBoundaryExtendedSpace = 0
+ var topBoundaryReset = false
+
+ override fun extendTopBoundary(space: Int) {
+ topBoundaryExtendedSpace = space
+ }
+
+ override fun resetTopBoundary() {
+ topBoundaryReset = true
+ }
+ }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt
deleted file mode 100644
index d417790..0000000
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.taskbar.rules
-
-import android.platform.test.flag.junit.FlagsParameterization
-import android.platform.test.flag.junit.SetFlagsRule
-import com.android.launcher3.Flags.FLAG_ENABLE_TASKBAR_PINNING
-import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
-import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
-import com.android.launcher3.util.DisplayController
-import org.junit.rules.RuleChain
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-
-/**
- * Rule that allows modifying the Taskbar pinned preferences.
- *
- * The original preference values are restored on teardown.
- *
- * If this rule is being used with [TaskbarUnitTestRule], make sure this rule is applied first.
- *
- * This rule is overkill if a test does not need to change the mode during Taskbar's lifecycle. If
- * the mode is static, use [TaskbarModeRule] instead, which forces the mode. A test can class can
- * declare both this rule and [TaskbarModeRule] but using both for a test method is unsupported.
- */
-class TaskbarPinningPreferenceRule(context: TaskbarWindowSandboxContext) : TestRule {
-
- private val setFlagsRule =
- SetFlagsRule(FlagsParameterization(mapOf(FLAG_ENABLE_TASKBAR_PINNING to true)))
- private val pinningRule = TaskbarPreferenceRule(context, TASKBAR_PINNING)
- private val desktopPinningRule = TaskbarPreferenceRule(context, TASKBAR_PINNING_IN_DESKTOP_MODE)
- private val ruleChain =
- RuleChain.outerRule(setFlagsRule).around(pinningRule).around(desktopPinningRule)
-
- var isPinned by pinningRule::value
- var isPinnedInDesktopMode by desktopPinningRule::value
-
- override fun apply(base: Statement, description: Description): Statement {
- return object : Statement() {
- override fun evaluate() {
- DisplayController.enableTaskbarModePreferenceForTests(true)
- try {
- ruleChain.apply(base, description).evaluate()
- } finally {
- DisplayController.enableTaskbarModePreferenceForTests(false)
- }
- }
- }
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt
deleted file mode 100644
index 977e7a5..0000000
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.taskbar.rules
-
-import com.android.launcher3.util.DisplayController
-import com.android.launcher3.util.LauncherMultivalentJUnit
-import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
-import com.android.launcher3.util.window.WindowManagerProxy
-import com.google.android.apps.nexuslauncher.deviceemulator.TestWindowManagerProxy
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.Description
-import org.junit.runner.RunWith
-import org.junit.runners.model.Statement
-
-@RunWith(LauncherMultivalentJUnit::class)
-@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
-class TaskbarPinningPreferenceRuleTest {
- @get:Rule val context = TaskbarWindowSandboxContext.create()
-
- private val preferenceRule = TaskbarPinningPreferenceRule(context)
-
- @Test
- fun testEnablePinning_verifyDisplayController() {
- onSetup {
- preferenceRule.isPinned = true
- preferenceRule.isPinnedInDesktopMode = false
- assertThat(DisplayController.isPinnedTaskbar(context)).isTrue()
- }
- }
-
- @Test
- fun testDisablePinning_verifyDisplayController() {
- onSetup {
- preferenceRule.isPinned = false
- preferenceRule.isPinnedInDesktopMode = false
- assertThat(DisplayController.isPinnedTaskbar(context)).isFalse()
- }
- }
-
- @Test
- fun testEnableDesktopPinning_verifyDisplayController() {
- context.putObject(
- WindowManagerProxy.INSTANCE,
- TestWindowManagerProxy(context).apply { isInDesktopMode = true },
- )
-
- onSetup {
- preferenceRule.isPinned = false
- preferenceRule.isPinnedInDesktopMode = true
- assertThat(DisplayController.isPinnedTaskbar(context)).isTrue()
- }
- }
-
- @Test
- fun testDisableDesktopPinning_verifyDisplayController() {
- context.putObject(
- WindowManagerProxy.INSTANCE,
- TestWindowManagerProxy(context).apply { isInDesktopMode = true },
- )
-
- onSetup {
- preferenceRule.isPinned = false
- preferenceRule.isPinnedInDesktopMode = false
- assertThat(DisplayController.isPinnedTaskbar(context)).isFalse()
- }
- }
-
- @Test
- fun testTearDown_afterTogglingPinnedPreference_preferenceReset() {
- val wasPinned = preferenceRule.isPinned
- onSetup { preferenceRule.isPinned = !preferenceRule.isPinned }
- assertThat(preferenceRule.isPinned).isEqualTo(wasPinned)
- }
-
- @Test
- fun testTearDown_afterTogglingDesktopPreference_preferenceReset() {
- val wasPinnedInDesktopMode = preferenceRule.isPinnedInDesktopMode
- onSetup { preferenceRule.isPinnedInDesktopMode = !preferenceRule.isPinnedInDesktopMode }
- assertThat(preferenceRule.isPinnedInDesktopMode).isEqualTo(wasPinnedInDesktopMode)
- }
-
- /** Executes [runTest] after the [preferenceRule] setup phase completes. */
- private fun onSetup(runTest: () -> Unit) {
- preferenceRule
- .apply(
- object : Statement() {
- override fun evaluate() = runTest()
- },
- DESCRIPTION,
- )
- .evaluate()
- }
-
- private companion object {
- private val DESCRIPTION =
- Description.createSuiteDescription(TaskbarPinningPreferenceRule::class.java)
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt
deleted file mode 100644
index e42ca9e..0000000
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.taskbar.rules
-
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.launcher3.ConstantItem
-import com.android.launcher3.LauncherPrefs
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-
-/**
- * Rule for modifying a Taskbar preference.
- *
- * The original preference value is restored on teardown.
- */
-class TaskbarPreferenceRule<T : Any>(
- private val context: TaskbarWindowSandboxContext,
- private val constantItem: ConstantItem<T>,
-) : TestRule {
-
- private val prefs: LauncherPrefs
- get() = LauncherPrefs.get(context)
-
- var value: T
- get() = prefs.get(constantItem)
- set(value) = getInstrumentation().runOnMainSync { prefs.put(constantItem, value) }
-
- override fun apply(base: Statement, description: Description): Statement {
- return object : Statement() {
- override fun evaluate() {
- val originalValue = value
- try {
- base.evaluate()
- } finally {
- value = originalValue
- }
- }
- }
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt
deleted file mode 100644
index b7e6fa3..0000000
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.taskbar.rules
-
-import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
-import com.android.launcher3.util.LauncherMultivalentJUnit
-import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.Description
-import org.junit.runner.RunWith
-import org.junit.runners.model.Statement
-
-@RunWith(LauncherMultivalentJUnit::class)
-@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
-class TaskbarPreferenceRuleTest {
-
- @get:Rule val context = TaskbarWindowSandboxContext.create()
- private val preferenceRule = TaskbarPreferenceRule(context, TASKBAR_PINNING)
-
- @Test
- fun testSetup_toggleBoolean_updatesPreferences() {
- val originalValue = preferenceRule.value
- onSetup {
- preferenceRule.value = !preferenceRule.value
- assertThat(preferenceRule.value).isNotEqualTo(originalValue)
- }
- }
-
- @Test
- fun testTeardown_afterTogglingBoolean_preferenceReset() {
- val originalValue = preferenceRule.value
- onSetup { preferenceRule.value = !preferenceRule.value }
- assertThat(preferenceRule.value).isEqualTo(originalValue)
- }
-
- private fun onSetup(runTest: () -> Unit) {
- preferenceRule
- .apply(
- object : Statement() {
- override fun evaluate() = runTest()
- },
- DESCRIPTION,
- )
- .evaluate()
- }
-
- private companion object {
- private val DESCRIPTION =
- Description.createSuiteDescription(TaskbarPreferenceRule::class.java)
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 741be50..2d3bfd6 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
@@ -16,19 +16,67 @@
package com.android.launcher3.taskbar.rules
+import android.content.Context
import android.content.ContextWrapper
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.FakeLauncherPrefs
+import com.android.launcher3.LauncherPrefs
import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
import com.android.launcher3.util.SandboxApplication
+import org.junit.rules.ExternalResource
+import org.junit.rules.RuleChain
import org.junit.rules.TestRule
-/** Sandbox Context for running Taskbar tests. */
-class TaskbarWindowSandboxContext private constructor(base: SandboxApplication) :
- ContextWrapper(base), ObjectSandbox by base, TestRule by base {
+/**
+ * [SandboxApplication] for running Taskbar tests.
+ *
+ * Tests need to run on a [VirtualDisplay] to avoid conflicting with Launcher's Taskbar on the
+ * [DEFAULT_DISPLAY] (i.e. test is executing on a device).
+ */
+class TaskbarWindowSandboxContext
+private constructor(base: SandboxApplication, val virtualDisplay: VirtualDisplay) :
+ ContextWrapper(base),
+ ObjectSandbox by base,
+ TestRule by RuleChain.outerRule(virtualDisplayRule(virtualDisplay)).around(base) {
+
+ init {
+ putObject(LauncherPrefs.INSTANCE, FakeLauncherPrefs(this))
+ }
companion object {
+ private const val VIRTUAL_DISPLAY_NAME = "TaskbarSandboxDisplay"
+
/** Creates a [SandboxApplication] for Taskbar tests. */
fun create(): TaskbarWindowSandboxContext {
- return TaskbarWindowSandboxContext(SandboxApplication())
+ val base = ApplicationProvider.getApplicationContext<Context>()
+ val displayManager = checkNotNull(base.getSystemService(DisplayManager::class.java))
+
+ // Create virtual display to avoid clashing with Taskbar on default display.
+ val virtualDisplay =
+ base.resources.displayMetrics.let {
+ displayManager.createVirtualDisplay(
+ VIRTUAL_DISPLAY_NAME,
+ it.widthPixels,
+ it.heightPixels,
+ it.densityDpi,
+ /* surface= */ null,
+ /* flags= */ 0,
+ )
+ }
+
+ return TaskbarWindowSandboxContext(
+ SandboxApplication(base.createDisplayContext(virtualDisplay.display)),
+ virtualDisplay,
+ )
}
}
}
+
+private fun virtualDisplayRule(virtualDisplay: VirtualDisplay): TestRule {
+ return object : ExternalResource() {
+ override fun after() = virtualDisplay.release()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContextTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContextTest.kt
new file mode 100644
index 0000000..69095e7
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContextTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar.rules
+
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.Description
+import org.junit.runner.RunWith
+import org.junit.runners.model.Statement
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023"])
+class TaskbarWindowSandboxContextTest {
+
+ @Test
+ fun testVirtualDisplay_releasedOnTeardown() {
+ val context = TaskbarWindowSandboxContext.create()
+ assertThat(context.virtualDisplay.token).isNotNull()
+
+ context
+ .apply(
+ object : Statement() {
+ override fun evaluate() = Unit
+ },
+ Description.createSuiteDescription(TaskbarWindowSandboxContextTest::class.java),
+ )
+ .evaluate()
+
+ assertThat(context.virtualDisplay.token).isNull()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt
index 0ae710f..b0db737 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt
@@ -41,7 +41,6 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.spy
-import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
@@ -56,10 +55,12 @@
private val testScope = TestScope(dispatcher)
private var pendingCallbacksWithDelays = mutableListOf<Long>()
+ private lateinit var pendingCommandsToExecute: MutableList<Runnable>
@Suppress("UNCHECKED_CAST")
@Before
fun setup() {
+ pendingCommandsToExecute = mutableListOf()
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_OVERVIEW_COMMAND_HELPER_TIMEOUT)
sut =
@@ -68,7 +69,8 @@
touchInteractionService = mock(),
overviewComponentObserver = mock(),
taskAnimationManager = mock(),
- dispatcherProvider = TestDispatcherProvider(dispatcher)
+ dispatcherProvider = TestDispatcherProvider(dispatcher),
+ uiExecutor = { runnable -> pendingCommandsToExecute += runnable },
)
)
@@ -94,12 +96,21 @@
pendingCallbacksWithDelays.add(delayInMillis)
}
+ /**
+ * This function runs all the pending commands from the Executor for testing purposes. Replacing
+ * the uiExecutor allows the test to execute the command queue manually, making it possible to
+ * assert each state of the commands in the queue individually.
+ */
+ private fun executePendingCommands() = pendingCommandsToExecute.forEach { it.run() }
+
@Test
fun whenFirstCommandIsAdded_executeCommandImmediately() =
testScope.runTest {
// Add command to queue
val commandInfo: CommandInfo = sut.addCommand(CommandType.HOME)!!
assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
+ executePendingCommands()
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.PROCESSING)
runCurrent()
assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
}
@@ -114,7 +125,7 @@
val commandInfo: CommandInfo = sut.addCommand(commandType)!!
assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
- runCurrent()
+ executePendingCommands()
assertThat(commandInfo.status).isEqualTo(CommandStatus.PROCESSING)
advanceTimeBy(200L)
@@ -135,12 +146,14 @@
val commandInfo2: CommandInfo = sut.addCommand(commandType2)!!
assertThat(commandInfo2.status).isEqualTo(CommandStatus.IDLE)
- runCurrent()
+ executePendingCommands()
assertThat(commandInfo1.status).isEqualTo(CommandStatus.PROCESSING)
assertThat(commandInfo2.status).isEqualTo(CommandStatus.IDLE)
advanceTimeBy(101L)
assertThat(commandInfo1.status).isEqualTo(CommandStatus.COMPLETED)
+
+ executePendingCommands()
assertThat(commandInfo2.status).isEqualTo(CommandStatus.PROCESSING)
advanceTimeBy(101L)
@@ -161,12 +174,14 @@
val commandInfo2: CommandInfo = sut.addCommand(commandType2)!!
assertThat(commandInfo2.status).isEqualTo(CommandStatus.IDLE)
- runCurrent()
+ executePendingCommands()
assertThat(commandInfo1.status).isEqualTo(CommandStatus.PROCESSING)
assertThat(commandInfo2.status).isEqualTo(CommandStatus.IDLE)
advanceTimeBy(QUEUE_TIMEOUT)
assertThat(commandInfo1.status).isEqualTo(CommandStatus.CANCELED)
+
+ executePendingCommands()
assertThat(commandInfo2.status).isEqualTo(CommandStatus.PROCESSING)
advanceTimeBy(101)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandlerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandlerTest.java
new file mode 100644
index 0000000..9018775
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandlerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.inputconsumers;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.quickstep.DeviceConfigWrapper;
+import com.android.quickstep.NavHandle;
+import com.android.quickstep.util.TestExtensions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NavHandleLongPressHandlerTest {
+
+ private NavHandleLongPressHandler mLongPressHandler;
+ @Mock private NavHandle mNavHandle;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mLongPressHandler = new NavHandleLongPressHandler(context);
+ }
+
+ @Test
+ public void testStartNavBarAnimation_flagDisabled() {
+ try (AutoCloseable flag = overrideAnimateLPNHFlag(false)) {
+ mLongPressHandler.startNavBarAnimation(mNavHandle);
+ verify(mNavHandle, never())
+ .animateNavBarLongPress(anyBoolean(), anyBoolean(), anyLong());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testStartNavBarAnimation_flagEnabled() {
+ try (AutoCloseable flag = overrideAnimateLPNHFlag(true)) {
+ mLongPressHandler.startNavBarAnimation(mNavHandle);
+ verify(mNavHandle).animateNavBarLongPress(anyBoolean(), anyBoolean(), anyLong());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private AutoCloseable overrideAnimateLPNHFlag(boolean value) {
+ return TestExtensions.overrideNavConfigFlag(
+ "ANIMATE_LPNH", value, () -> DeviceConfigWrapper.get().getAnimateLpnh());
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
index c18f604..98a3607 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -92,14 +92,15 @@
when(mTopTaskTracker.getCachedTopTask(anyBoolean())).thenReturn(mTaskInfo);
when(mDeviceState.getSquaredTouchSlop()).thenReturn(SQUARED_TOUCH_SLOP);
when(mDelegate.allowInterceptByParent()).thenReturn(true);
- MAIN_EXECUTOR.getHandler().removeCallbacks(mLongPressRunnable);
mLongPressTriggered.set(false);
when(mNavHandleLongPressHandler.getLongPressRunnable(any())).thenReturn(mLongPressRunnable);
initializeObjectUnderTest();
}
@After
- public void tearDown() {
+ public void tearDown() throws Exception {
+ MAIN_EXECUTOR.getHandler().removeCallbacks(mLongPressRunnable);
+ MAIN_EXECUTOR.submit(() -> null).get();
mContext.onDestroy();
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
index 0a60774..7c48ea4 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
@@ -34,7 +34,6 @@
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_DISABLED
import com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY
-import com.android.launcher3.util.DaggerSingletonTracker
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -63,7 +62,6 @@
@Mock private lateinit var mMockLogger: StatsLogManager.StatsLogger
@Captor private lateinit var mEventCaptor: ArgumentCaptor<StatsLogManager.EventEnum>
- @Mock private lateinit var mTracker: DaggerSingletonTracker
private var mDefaultThemedIcons = false
private var mDefaultAllowRotation = false
@@ -81,7 +79,7 @@
// To match the default value of ALLOW_ROTATION
LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = false)
- mSystemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager, mTracker)
+ mSystemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager)
}
@After
@@ -92,7 +90,7 @@
@Test
fun loggingPrefs_correctDefaultValue() {
- val systemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager, mTracker)
+ val systemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager)
assertThat(systemUnderTest.loggingPrefs[ALLOW_ROTATION_PREFERENCE_KEY]!!.defaultValue)
.isFalse()
@@ -119,7 +117,7 @@
LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = true)
// This a new object so the values of mLoggablePrefs will be different
- SettingsChangeLogger(mContext, mStatsLogManager, mTracker).logSnapshot(mInstanceId)
+ SettingsChangeLogger(mContext, mStatsLogManager).logSnapshot(mInstanceId)
verify(mMockLogger, atLeastOnce()).log(mEventCaptor.capture())
val capturedEvents = mEventCaptor.allValues
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java
new file mode 100644
index 0000000..543ffe6
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/ContextualSearchInvokerTest.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_KEYGUARD;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_NOTIFICATION_SHADE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_ATTEMPTED_SPLITSCREEN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_FAILED_NOT_AVAILABLE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_FAILED_SETTING_DISABLED;
+import static com.android.quickstep.util.ContextualSearchInvoker.KEYGUARD_SHOWING_SYSUI_FLAGS;
+import static com.android.quickstep.util.ContextualSearchInvoker.SHADE_EXPANDED_SYSUI_FLAGS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.contextualsearch.ContextualSearchManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.quickstep.DeviceConfigWrapper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TopTaskTracker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Robolectric unit tests for {@link ContextualSearchInvoker}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ContextualSearchInvokerTest {
+
+ private static final int CONTEXTUAL_SEARCH_ENTRY_POINT = 123;
+
+ private @Mock PackageManager mMockPackageManager;
+ private @Mock ContextualSearchStateManager mMockStateManager;
+ private @Mock TopTaskTracker mMockTopTaskTracker;
+ private @Mock SystemUiProxy mMockSystemUiProxy;
+ private @Mock StatsLogManager mMockStatsLogManager;
+ private @Mock StatsLogManager.StatsLogger mMockStatsLogger;
+ private @Mock ContextualSearchHapticManager mMockContextualSearchHapticManager;
+ private @Mock ContextualSearchManager mMockContextualSearchManager;
+ private ContextualSearchInvoker mContextualSearchInvoker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockPackageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)).thenReturn(true);
+ Context context = spy(getApplicationContext());
+ doReturn(mMockPackageManager).when(context).getPackageManager();
+ when(mMockSystemUiProxy.getLastSystemUiStateFlags()).thenReturn(0L);
+ when(mMockTopTaskTracker.getRunningSplitTaskIds()).thenReturn(new int[]{});
+ when(mMockStateManager.isContextualSearchIntentAvailable()).thenReturn(true);
+ when(mMockStateManager.isContextualSearchSettingEnabled()).thenReturn(true);
+ when(mMockStatsLogManager.logger()).thenReturn(mMockStatsLogger);
+
+ mContextualSearchInvoker = new ContextualSearchInvoker(context, mMockStateManager,
+ mMockTopTaskTracker, mMockSystemUiProxy, mMockStatsLogManager,
+ mMockContextualSearchHapticManager, mMockContextualSearchManager);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_contextualSearchFeatureIsNotAvailable() {
+ when(mMockPackageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)).thenReturn(false);
+
+ assertFalse("Expected invocation to fail when feature is unavailable",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_contextualSearchIntentIsAvailable() {
+ assertTrue("Expected invocation checks to succeed",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ verifyNoMoreInteractions(mMockStatsLogManager);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_contextualSearchIntentIsNotAvailable() {
+ when(mMockStateManager.isContextualSearchIntentAvailable()).thenReturn(false);
+
+ assertFalse("Expected invocation to fail when feature is unavailable",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_OMNI_FAILED_NOT_AVAILABLE);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_settingDisabled() {
+ when(mMockStateManager.isContextualSearchSettingEnabled()).thenReturn(false);
+
+ assertFalse("Expected invocation checks to fail when setting is disabled",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_OMNI_FAILED_SETTING_DISABLED);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_notificationShadeIsShowing() {
+ when(mMockSystemUiProxy.getLastSystemUiStateFlags()).thenReturn(SHADE_EXPANDED_SYSUI_FLAGS);
+
+ assertFalse("Expected invocation checks to fail when notification shade is showing",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_NOTIFICATION_SHADE);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_keyguardIsShowing() {
+ when(mMockSystemUiProxy.getLastSystemUiStateFlags()).thenReturn(
+ KEYGUARD_SHOWING_SYSUI_FLAGS);
+
+ assertFalse("Expected invocation checks to fail when keyguard is showing",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_KEYGUARD);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_isInSplitScreen_disallowed() {
+ when(mMockStateManager.isInvocationAllowedInSplitscreen()).thenReturn(false);
+ when(mMockTopTaskTracker.getRunningSplitTaskIds()).thenReturn(new int[]{1, 2, 3});
+
+ assertFalse("Expected invocation checks to fail over split screen",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ // Attempt is logged regardless.
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_SPLITSCREEN);
+ }
+
+ @Test
+ public void runContextualSearchInvocationChecksAndLogFailures_isInSplitScreen_allowed() {
+ when(mMockStateManager.isInvocationAllowedInSplitscreen()).thenReturn(true);
+ when(mMockTopTaskTracker.getRunningSplitTaskIds()).thenReturn(new int[]{1, 2, 3});
+
+ assertTrue("Expected invocation checks to succeed over split screen",
+ mContextualSearchInvoker.runContextualSearchInvocationChecksAndLogFailures());
+
+ // Attempt is logged regardless.
+ verify(mMockStatsLogger).log(LAUNCHER_LAUNCH_OMNI_ATTEMPTED_SPLITSCREEN);
+ }
+
+ @Test
+ public void invokeContextualSearchUncheckedWithHaptic_cssIsAvailable_commitHapticEnabled() {
+ try (AutoCloseable flag = overrideSearchHapticCommitFlag(true)) {
+ assertTrue("Expected invocation unchecked to succeed",
+ mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
+ CONTEXTUAL_SEARCH_ENTRY_POINT));
+ verify(mMockContextualSearchHapticManager).vibrateForSearch();
+ verify(mMockContextualSearchManager).startContextualSearch(
+ CONTEXTUAL_SEARCH_ENTRY_POINT);
+ verifyNoMoreInteractions(mMockStatsLogManager);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void invokeContextualSearchUncheckedWithHaptic_cssIsAvailable_commitHapticDisabled() {
+ try (AutoCloseable flag = overrideSearchHapticCommitFlag(false)) {
+ assertTrue("Expected invocation unchecked to succeed",
+ mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
+ CONTEXTUAL_SEARCH_ENTRY_POINT));
+ verify(mMockContextualSearchHapticManager, never()).vibrateForSearch();
+ verify(mMockContextualSearchManager).startContextualSearch(
+ CONTEXTUAL_SEARCH_ENTRY_POINT);
+ verifyNoMoreInteractions(mMockStatsLogManager);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void invokeContextualSearchUncheckedWithHaptic_cssIsNotAvailable_commitHapticEnabled() {
+ when(mMockStateManager.isContextualSearchIntentAvailable()).thenReturn(false);
+
+ try (AutoCloseable flag = overrideSearchHapticCommitFlag(true)) {
+ // Still expect true since this method doesn't run the checks.
+ assertTrue("Expected invocation unchecked to succeed",
+ mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
+ CONTEXTUAL_SEARCH_ENTRY_POINT));
+ // Still vibrate based on the flag.
+ verify(mMockContextualSearchHapticManager).vibrateForSearch();
+ verify(mMockContextualSearchManager).startContextualSearch(
+ CONTEXTUAL_SEARCH_ENTRY_POINT);
+ verifyNoMoreInteractions(mMockStatsLogManager);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ @Test
+ public void invokeContextualSearchUncheckedWithHaptic_cssIsNotAvailable_commitHapticDisabled() {
+ when(mMockStateManager.isContextualSearchIntentAvailable()).thenReturn(false);
+
+ try (AutoCloseable flag = overrideSearchHapticCommitFlag(false)) {
+ // Still expect true since this method doesn't run the checks.
+ assertTrue("Expected ContextualSearch invocation unchecked to succeed",
+ mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
+ CONTEXTUAL_SEARCH_ENTRY_POINT));
+ // Still don't vibrate based on the flag.
+ verify(mMockContextualSearchHapticManager, never()).vibrateForSearch();
+ verify(mMockContextualSearchManager).startContextualSearch(
+ CONTEXTUAL_SEARCH_ENTRY_POINT);
+ verifyNoMoreInteractions(mMockStatsLogManager);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private AutoCloseable overrideSearchHapticCommitFlag(boolean value) {
+ return TestExtensions.overrideNavConfigFlag(
+ "ENABLE_SEARCH_HAPTIC_COMMIT",
+ value,
+ () -> DeviceConfigWrapper.get().getEnableSearchHapticCommit());
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
new file mode 100644
index 0000000..8968b9c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.content.ComponentName
+import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.uioverrides.QuickstepLauncher
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay
+import com.android.quickstep.task.thumbnail.TaskThumbnailView
+import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.TaskContainer
+import com.android.quickstep.views.TaskThumbnailViewDeprecated
+import com.android.quickstep.views.TaskView
+import com.android.quickstep.views.TaskViewIcon
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+/** Test for ExternalDisplaySystemShortcut */
+class ExternalDisplaySystemShortcutTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ private val launcher: QuickstepLauncher = mock()
+ private val statsLogManager: StatsLogManager = mock()
+ private val statsLogger: StatsLogManager.StatsLogger = mock()
+ private val recentsView: LauncherRecentsView = mock()
+ private val taskView: TaskView = mock()
+ private val workspaceItemInfo: WorkspaceItemInfo = mock()
+ private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
+ private val iconView: TaskViewIcon = mock()
+ private val transformingTouchDelegate: TransformingTouchDelegate = mock()
+ private val factory: TaskShortcutFactory =
+ ExternalDisplaySystemShortcut.createFactory(abstractFloatingViewHelper)
+ private val overlayFactory: TaskOverlayFactory = mock()
+ private val overlay: TaskOverlay<*> = mock()
+
+ private lateinit var mockitoSession: StaticMockitoSession
+
+ @Before
+ fun setUp() {
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
+ ExtendedMockito.doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ whenever(overlayFactory.createOverlay(any())).thenReturn(overlay)
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @EnableFlags(Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT)
+ fun createExternalDisplayTaskShortcut_desktopModeDisabled() {
+ val task = createTask()
+ val taskContainer = createTaskContainer(task)
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT,
+ )
+ fun createExternalDisplayTaskShortcut_desktopModeEnabled_deviceNotSupported() {
+ ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+ val taskContainer = createTaskContainer(createTask())
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT,
+ )
+ fun createExternalDisplayTaskShortcut_desktopModeEnabled_deviceNotSupported_overrideEnabled() {
+ ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ ExtendedMockito.doReturn(false).`when` { DesktopModeStatus.enforceDeviceRestrictions() }
+
+ val taskContainer = spy(createTaskContainer(createTask()))
+ doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_MOVE_TO_EXTERNAL_DISPLAY_SHORTCUT,
+ )
+ fun externalDisplaySystemShortcutClicked() {
+ val task = createTask()
+ val taskContainer = spy(createTaskContainer(task))
+
+ whenever(launcher.getOverviewPanel<LauncherRecentsView>()).thenReturn(recentsView)
+ whenever(launcher.statsLogManager).thenReturn(statsLogManager)
+ whenever(statsLogManager.logger()).thenReturn(statsLogger)
+ whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
+ whenever(recentsView.moveTaskToExternalDisplay(any(), any())).thenAnswer {
+ val successCallback = it.getArgument<Runnable>(1)
+ successCallback.run()
+ }
+ doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).hasSize(1)
+ assertThat(shortcuts!!.first()).isInstanceOf(ExternalDisplaySystemShortcut::class.java)
+
+ val externalDisplayShortcut = shortcuts.first() as ExternalDisplaySystemShortcut
+
+ externalDisplayShortcut.onClick(taskView)
+
+ val allTypesExceptRebindSafe =
+ AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv()
+ verify(abstractFloatingViewHelper).closeOpenViews(launcher, true, allTypesExceptRebindSafe)
+ verify(recentsView).moveTaskToExternalDisplay(eq(taskContainer), any())
+ verify(statsLogger).withItemInfo(workspaceItemInfo)
+ verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_EXTERNAL_DISPLAY_TAP)
+ }
+
+ private fun createTask(): Task = Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000))
+
+ private fun createTaskContainer(task: Task): TaskContainer {
+ val snapshotView =
+ if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
+ else mock<TaskThumbnailViewDeprecated>()
+ return TaskContainer(
+ taskView,
+ task,
+ snapshotView,
+ iconView,
+ transformingTouchDelegate,
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+ digitalWellBeingToast = null,
+ showWindowsView = null,
+ overlayFactory,
+ )
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index 800fd4a..b15b78e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -56,8 +56,6 @@
@Override
public void setUp() throws Exception {
super.setUp();
- initialize(this);
-
createAndStartPrivateProfileUser();
mDevice.pressHome();
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index ec07b93..633a575 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -17,8 +17,8 @@
package com.android.quickstep;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -73,7 +73,8 @@
final ArgumentCaptor<ActivityOptions> optionsCaptor =
ArgumentCaptor.forClass(ActivityOptions.class);
- verify(mSystemUiProxy).startRecentsActivity(any(), optionsCaptor.capture(), any());
+ verify(mSystemUiProxy)
+ .startRecentsActivity(any(), optionsCaptor.capture(), any(), anyBoolean());
assertEquals(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS,
optionsCaptor.getValue().getPendingIntentBackgroundActivityStartMode());
}
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/WindowAnimatorTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/WindowAnimatorTest.kt
new file mode 100644
index 0000000..e5e6df3
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/desktop/WindowAnimatorTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 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.desktop
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.android.app.animation.Interpolators
+import com.android.launcher3.desktop.WindowAnimator
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WindowAnimatorTest {
+
+ private val context = mock<Context>()
+ private val resources = mock<Resources>()
+ private val transaction = mock<SurfaceControl.Transaction>()
+ private val change = mock<TransitionInfo.Change>()
+ private val leash = mock<SurfaceControl>()
+
+ private val displayMetrics = DisplayMetrics().apply { density = 1f }
+
+ @Before
+ fun setup() {
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.displayMetrics).thenReturn(displayMetrics)
+ whenever(change.leash).thenReturn(leash)
+ whenever(change.startAbsBounds).thenReturn(START_BOUNDS)
+ whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ }
+
+ @Test
+ fun createBoundsAnimator_returnsCorrectDefaultAnimatorParams() = runOnUiThread {
+ val boundsAnimParams =
+ WindowAnimator.BoundsAnimationParams(
+ durationMs = 100L,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
+ )
+
+ val valueAnimator =
+ WindowAnimator.createBoundsAnimator(context, boundsAnimParams, change, transaction)
+
+ assertThat(valueAnimator.duration).isEqualTo(100L)
+ assertThat(valueAnimator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
+ assertStartAndEndBounds(valueAnimator, startBounds = START_BOUNDS, endBounds = START_BOUNDS)
+ }
+
+ @Test
+ fun createBoundsAnimator_startScaleAndOffset_returnsCorrectBounds() = runOnUiThread {
+ val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+ whenever(change.startAbsBounds).thenReturn(bounds)
+ val boundsAnimParams =
+ WindowAnimator.BoundsAnimationParams(
+ durationMs = 100L,
+ startOffsetYDp = 10f,
+ startScale = 0.5f,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
+ )
+
+ val valueAnimator =
+ WindowAnimator.createBoundsAnimator(context, boundsAnimParams, change, transaction)
+
+ assertStartAndEndBounds(
+ valueAnimator,
+ startBounds =
+ Rect(/* left= */ 150, /* top= */ 260, /* right= */ 250, /* bottom= */ 360),
+ endBounds = bounds,
+ )
+ }
+
+ @Test
+ fun createBoundsAnimator_endScaleAndOffset_returnsCorrectBounds() = runOnUiThread {
+ val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+ whenever(change.startAbsBounds).thenReturn(bounds)
+ val boundsAnimParams =
+ WindowAnimator.BoundsAnimationParams(
+ durationMs = 100L,
+ endOffsetYDp = 10f,
+ endScale = 0.5f,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
+ )
+
+ val valueAnimator =
+ WindowAnimator.createBoundsAnimator(context, boundsAnimParams, change, transaction)
+
+ assertStartAndEndBounds(
+ valueAnimator,
+ startBounds = bounds,
+ endBounds = Rect(/* left= */ 150, /* top= */ 260, /* right= */ 250, /* bottom= */ 360),
+ )
+ }
+
+ private fun assertStartAndEndBounds(
+ valueAnimator: ValueAnimator,
+ startBounds: Rect,
+ endBounds: Rect,
+ ) {
+ valueAnimator.start()
+ valueAnimator.animatedValue
+ assertThat(valueAnimator.animatedValue).isEqualTo(startBounds)
+ valueAnimator.end()
+ assertThat(valueAnimator.animatedValue).isEqualTo(endBounds)
+ }
+
+ companion object {
+ private val START_BOUNDS =
+ Rect(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
+ }
+}
diff --git a/res/drawable/ic_private_profile_divider_badge.xml b/res/drawable/ic_private_profile_divider_badge.xml
new file mode 100644
index 0000000..07c740d
--- /dev/null
+++ b/res/drawable/ic_private_profile_divider_badge.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ <group>
+ <path
+ android:pathData="M5,9L15,9A1,1 0,0 1,16 10L16,10A1,1 0,0 1,15 11L5,11A1,1 0,0 1,4 10L4,10A1,1 0,0 1,5 9z"
+ android:fillColor="?attr/materialColorOnSurface"/>
+ </group>
+</vector>
diff --git a/res/drawable/ic_private_profile_letter_list_fast_scroller_badge.xml b/res/drawable/ic_private_profile_letter_list_fast_scroller_badge.xml
new file mode 100644
index 0000000..8d12598
--- /dev/null
+++ b/res/drawable/ic_private_profile_letter_list_fast_scroller_badge.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="12dp"
+ android:height="15dp"
+ android:viewportWidth="12"
+ android:viewportHeight="15">
+ <path
+ android:pathData="M5.952,0.911L0.645,2.902V6.942C0.645,10.292 2.907,13.417 5.952,14.18C8.997,13.417 11.26,10.292 11.26,6.942V2.902L5.952,0.911ZM7.943,9.536V10.863H6.616V11.526H5.289V8.103C4.333,7.818 3.63,6.942 3.63,5.887C3.63,4.607 4.672,3.565 5.952,3.565C7.233,3.565 8.274,4.607 8.274,5.887C8.274,6.935 7.571,7.818 6.616,8.103V9.536H7.943Z"
+ android:fillColor="#3C4043"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M5.952,6.882C6.502,6.882 6.947,6.436 6.947,5.887C6.947,5.337 6.502,4.892 5.952,4.892C5.403,4.892 4.957,5.337 4.957,5.887C4.957,6.436 5.403,6.882 5.952,6.882Z"
+ android:fillColor="#3C4043"/>
+</vector>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9211e76..0d78d90 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -174,7 +174,7 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Genveje"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Afvis"</string>
<string name="accessibility_close" msgid="2277148124685870734">"Luk"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlige"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Arbejdsapps har badges og kan ses af din it-administrator"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index e6d6442..02389c4 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -120,7 +120,7 @@
<string name="app_pair_name_format" msgid="8134106404716224054">"Emparellamento de aplicacións: <xliff:g id="APP1">%1$s</xliff:g> e <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estilo e fondo de pantalla"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Editar pantalla de inicio"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Axustes de Inicio"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configuración da pantalla de inicio"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permitir xirar a pantalla de inicio"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 91530b5..786cea1 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -21,7 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="work_folder_name" msgid="3753320833950115786">"ಕೆಲಸ"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
+ <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>
@@ -103,7 +103,7 @@
<string name="gadget_error_text" msgid="740356548025791839">"ವಿಜೆಟ್ ಅನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"ವಿಜೆಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="gadget_complete_setup_text" msgid="309040266978007925">"ಸೆಟಪ್ ಪೂರ್ಣಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಆ್ಯಪ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="folder_hint_text" msgid="5174843001373488816">"ಹೆಸರನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name} ಆ್ಯಪ್ # ಅಧಿಸೂಚನೆಯನ್ನು ಹೊಂದಿದೆ}one{{app_name} ಆ್ಯಪ್ # ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ}other{{app_name} ಆ್ಯಪ್ # ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ}}"</string>
@@ -120,7 +120,7 @@
<string name="app_pair_name_format" msgid="8134106404716224054">"ಆ್ಯಪ್ ಜೋಡಿ: <xliff:g id="APP1">%1$s</xliff:g> ಮತ್ತು <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"ವಾಲ್ಪೇಪರ್ ಮತ್ತು ಶೈಲಿ"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ಹೋಮ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್ ತಿರುಗಿಸಿದಾಗ"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 701e64a..504218b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -85,7 +85,6 @@
<string name="wallpaper_picker_package" translatable="false"></string>
<string name="local_colors_extraction_class" translatable="false"></string>
<string name="search_session_manager_class" translatable="false"></string>
- <string name="plugin_manager_wrapper_class" translatable="false"></string>
<!-- Scalable Grid configuration -->
<!-- This is a float because it is converted to dp later in DeviceProfile -->
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 2e75261..3774ae3 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -306,6 +306,10 @@
removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
}
+ public boolean isPaused() {
+ return !hasBeenResumed() && (mActivityFlags & ACTIVITY_STATE_DEFERRED_RESUMED) == 0;
+ }
+
/**
* Sets the activity to appear as resumed.
*/
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 76dc770..5acff06 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -1130,6 +1130,9 @@
if (itemInfo.isDisabled()) {
setContentDescription(getContext().getString(R.string.disabled_app_label,
itemInfo.contentDescription));
+ } else if (itemInfo instanceof WorkspaceItemInfo wai && wai.isArchived()) {
+ setContentDescription(
+ getContext().getString(R.string.app_archived_title, itemInfo.title));
} else if (hasDot()) {
int count = mDotInfo.getNotificationCount();
setContentDescription(
@@ -1142,8 +1145,16 @@
}
private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
- if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_ARCHIVED) != 0 && progressLevel == 0) {
- setContentDescription(getContext().getString(R.string.app_archived_title, info.title));
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_ARCHIVED) != 0
+ && progressLevel == 0) {
+ if (mIcon instanceof PreloadIconDrawable) {
+ // Tell user that download is pending and not to tap to download again.
+ setContentDescription(getContext().getString(
+ R.string.app_waiting_download_title, info.title));
+ } else {
+ setContentDescription(getContext().getString(
+ R.string.app_archived_title, info.title));
+ }
} else if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
!= 0) {
String percentageString = NumberFormat.getPercentInstance()
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 1e7fd7f..8862550 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1829,19 +1829,14 @@
* Returns the new border space that should be used between hotseat icons after adjusting it to
* the bubble bar.
*
+ * <p>Does not check for visible bubbles persistence, so caller should call
+ * {@link #shouldAdjustHotseatForBubbleBar} first.
+ *
* <p>If there's no adjustment needed, this method returns {@code 0}.
+ * @see #shouldAdjustHotseatForBubbleBar(Context, boolean)
*/
public float getHotseatAdjustedBorderSpaceForBubbleBar(Context context) {
- // only need to adjust when QSB is on top of the hotseat.
- if (isQsbInline) {
- return 0;
- }
-
- // no need to adjust if there's enough space for the bubble bar to the right of the hotseat.
- if (getHotseatLayoutPadding(context).right > mBubbleBarSpaceThresholdPx) {
- return 0;
- }
-
+ if (!shouldAdjustHotseatForBubbleBar(context)) return 0;
// The adjustment is shrinking the hotseat's width by 1 icon on either side.
int iconsWidth =
iconSizePx * numShownHotseatIcons + hotseatBorderSpace * (numShownHotseatIcons - 1);
@@ -1851,6 +1846,33 @@
}
/**
+ * Returns the hotseat icon translation X for the cellX index.
+ *
+ * <p>Does not check for visible bubbles persistence, so caller should call
+ * {@link #shouldAdjustHotseatForBubbleBar} first.
+ *
+ * <p>If there's no adjustment needed, this method returns {@code 0}.
+ * @see #shouldAdjustHotseatForBubbleBar(Context, boolean)
+ */
+ public float getHotseatAdjustedTranslation(Context context, int cellX) {
+ if (!shouldAdjustHotseatForBubbleBar(context)) return 0;
+ float borderSpace = getHotseatAdjustedBorderSpaceForBubbleBar(context);
+ float borderSpaceDelta = borderSpace - hotseatBorderSpace;
+ return iconSizePx + cellX * borderSpaceDelta;
+ }
+
+ /** Returns whether hotseat should be adjusted for the bubble bar. */
+ public boolean shouldAdjustHotseatForBubbleBar(Context context, boolean hasBubbles) {
+ return hasBubbles && shouldAdjustHotseatForBubbleBar(context);
+ }
+
+ private boolean shouldAdjustHotseatForBubbleBar(Context context) {
+ // only need to adjust if bubble bar is enabled, when QSB is on top of the hotseat and
+ // there's not enough space for the bubble bar to the right of the hotseat.
+ return !isQsbInline && getHotseatLayoutPadding(context).right <= mBubbleBarSpaceThresholdPx;
+ }
+
+ /**
* Returns the padding for hotseat view
*/
public Rect getHotseatLayoutPadding(Context context) {
@@ -2333,18 +2355,23 @@
/**
* Returns whether Taskbar and Hotseat should adjust horizontally on bubble bar location update.
*/
- public boolean shouldAdjustHotseatOnBubblesLocationUpdate(Context context) {
+ public boolean shouldAdjustHotseatOnNavBarLocationUpdate(Context context) {
return enableBubbleBar()
&& enableBubbleBarInPersistentTaskBar()
&& !DisplayController.getNavigationMode(context).hasGestures;
}
/** Returns hotseat translation X for the bubble bar position. */
- public int getHotseatTranslationXForBubbleBar(boolean isNavbarOnRight, boolean isRtl) {
- if (isNavbarOnRight) {
- return isRtl ? -navButtonsLayoutWidthPx : 0;
+ public int getHotseatTranslationXForNavBar(Context context, boolean isBubblesOnLeft) {
+ if (shouldAdjustHotseatOnNavBarLocationUpdate(context)) {
+ boolean isRtl = Utilities.isRtl(context.getResources());
+ if (isBubblesOnLeft) {
+ return isRtl ? -navButtonsLayoutWidthPx : 0;
+ } else {
+ return isRtl ? 0 : navButtonsLayoutWidthPx;
+ }
} else {
- return isRtl ? 0 : navButtonsLayoutWidthPx;
+ return 0;
}
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ae4c122..6b478be 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -149,12 +149,9 @@
DeviceProfile dp = mActivity.getDeviceProfile();
if (bubbleBarEnabled) {
- float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
- if (hasBubbles && Float.compare(adjustedBorderSpace, 0f) != 0) {
- getShortcutsAndWidgets().setTranslationProvider(cellX -> {
- float borderSpaceDelta = adjustedBorderSpace - dp.hotseatBorderSpace;
- return dp.iconSizePx + cellX * borderSpaceDelta;
- });
+ if (dp.shouldAdjustHotseatForBubbleBar(getContext(), hasBubbles)) {
+ getShortcutsAndWidgets().setTranslationProvider(
+ cellX -> dp.getHotseatAdjustedTranslation(getContext(), cellX));
if (mQsb instanceof HorizontalInsettableView) {
HorizontalInsettableView insettableQsb = (HorizontalInsettableView) mQsb;
final float insetFraction = (float) dp.iconSizePx / dp.hotseatQsbWidth;
@@ -189,25 +186,24 @@
*/
public void adjustForBubbleBar(boolean isBubbleBarVisible) {
DeviceProfile dp = mActivity.getDeviceProfile();
- float adjustedBorderSpace = dp.getHotseatAdjustedBorderSpaceForBubbleBar(getContext());
- if (Float.compare(adjustedBorderSpace, 0f) == 0) {
+ if (!dp.shouldAdjustHotseatForBubbleBar(getContext(), isBubbleBarVisible)) {
return;
}
ShortcutAndWidgetContainer icons = getShortcutsAndWidgets();
AnimatorSet animatorSet = new AnimatorSet();
- float borderSpaceDelta = adjustedBorderSpace - dp.hotseatBorderSpace;
// update the translation provider for future layout passes of hotseat icons.
if (isBubbleBarVisible) {
- icons.setTranslationProvider(cellX -> dp.iconSizePx + cellX * borderSpaceDelta);
+ icons.setTranslationProvider(
+ cellX -> dp.getHotseatAdjustedTranslation(getContext(), cellX));
} else {
icons.setTranslationProvider(null);
}
for (int i = 0; i < icons.getChildCount(); i++) {
View child = icons.getChildAt(i);
- float tx = isBubbleBarVisible ? dp.iconSizePx + i * borderSpaceDelta : 0;
+ float tx = isBubbleBarVisible ? dp.getHotseatAdjustedTranslation(getContext(), i) : 0;
if (child instanceof Reorderable) {
MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
animatorSet.play(
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 200d5a7..a8840fe 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -412,6 +412,7 @@
private final List<BackPressHandler> mBackPressedHandlers = new ArrayList<>();
private boolean mIsColdStartupAfterReboot;
+ private boolean mForceConfigUpdate;
private boolean mIsNaturalScrollingEnabled;
@@ -756,7 +757,7 @@
protected void onHandleConfigurationChanged() {
Trace.beginSection("Launcher#onHandleconfigurationChanged");
try {
- if (!initDeviceProfile(mDeviceProfile.inv)) {
+ if (!initDeviceProfile(mDeviceProfile.inv) && !mForceConfigUpdate) {
return;
}
@@ -770,6 +771,7 @@
mModel.rebindCallbacks();
updateDisallowBack();
} finally {
+ mForceConfigUpdate = false;
Trace.endSection();
}
}
@@ -3151,6 +3153,13 @@
return mAnimationCoordinator;
}
+ /**
+ * Set to force config update when set to true next time onHandleConfigurationChanged is called.
+ */
+ public void setForceConfigUpdate(boolean forceConfigUpdate) {
+ mForceConfigUpdate = forceConfigUpdate;
+ }
+
@Override
public View.OnLongClickListener getAllAppsItemLongClickListener() {
return ItemLongClickListener.INSTANCE_ALL_APPS;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 05a3a52..42a28d6 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -176,7 +176,7 @@
() -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
InstallSessionTracker installSessionTracker =
- InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
+ InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(callbacks);
mOnTerminateCallback.add(installSessionTracker::unregister);
});
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index e7b9d89..a013eaa 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -16,10 +16,8 @@
package com.android.launcher3
import android.app.admin.DevicePolicyManager
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageInstaller
import android.content.pm.ShortcutInfo
import android.os.UserHandle
import android.text.TextUtils
@@ -29,7 +27,6 @@
import com.android.launcher3.celllayout.CellPosMapper
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.icons.IconCache
-import com.android.launcher3.icons.cache.BaseIconCache
import com.android.launcher3.model.AddWorkspaceItemsTask
import com.android.launcher3.model.AllAppsList
import com.android.launcher3.model.BaseLauncherBinder
@@ -42,23 +39,17 @@
import com.android.launcher3.model.ModelLauncherCallbacks
import com.android.launcher3.model.ModelTaskController
import com.android.launcher3.model.ModelWriter
-import com.android.launcher3.model.PackageInstallStateChangedTask
import com.android.launcher3.model.PackageUpdatedTask
import com.android.launcher3.model.ReloadStringCacheTask
import com.android.launcher3.model.ShortcutsChangedTask
import com.android.launcher3.model.UserLockStateChangedTask
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
-import com.android.launcher3.pm.InstallSessionTracker
-import com.android.launcher3.pm.PackageInstallInfo
import com.android.launcher3.pm.UserCache
import com.android.launcher3.shortcuts.ShortcutRequest
import com.android.launcher3.testing.shared.TestProtocol.sDebugTracing
-import com.android.launcher3.util.ApplicationInfoWrapper
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
-import com.android.launcher3.util.IntSet
-import com.android.launcher3.util.ItemInfoMatcher
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.util.Preconditions
@@ -66,7 +57,6 @@
import java.io.PrintWriter
import java.util.concurrent.CancellationException
import java.util.function.Consumer
-import java.util.function.Supplier
/**
* Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -80,7 +70,7 @@
private val appFilter: AppFilter,
private val mPmHelper: PackageManagerHelper,
isPrimaryInstance: Boolean,
-) : InstallSessionTracker.Callback {
+) {
private val mCallbacksList = ArrayList<BgDataModel.Callbacks>(1)
@@ -156,10 +146,10 @@
fun onAppIconChanged(packageName: String, user: UserHandle) {
// Update the icon for the calendar package
enqueueModelUpdateTask(PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageName))
- val pinnedShortcuts: List<ShortcutInfo> =
- ShortcutRequest(context, user).forPackage(packageName).query(ShortcutRequest.PINNED)
- if (pinnedShortcuts.isNotEmpty()) {
- enqueueModelUpdateTask(ShortcutsChangedTask(packageName, pinnedShortcuts, user, false))
+ ShortcutRequest(context, user).forPackage(packageName).query(ShortcutRequest.PINNED).let {
+ if (it.isNotEmpty()) {
+ enqueueModelUpdateTask(ShortcutsChangedTask(packageName, it, user, false))
+ }
}
}
@@ -176,14 +166,13 @@
fun onBroadcastIntent(intent: Intent) {
if (DEBUG_RECEIVER || sDebugTracing) Log.d(TAG, "onReceive intent=$intent")
- val action = intent.action
- if (Intent.ACTION_LOCALE_CHANGED == action) {
- // If we have changed locale we need to clear out the labels in all apps/workspace.
- forceReload()
- } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED == action) {
- enqueueModelUpdateTask(ReloadStringCacheTask(this.modelDelegate))
- } else if (BuildConfig.IS_STUDIO_BUILD && LauncherAppState.ACTION_FORCE_ROLOAD == action) {
- forceReload()
+ when (intent.action) {
+ Intent.ACTION_LOCALE_CHANGED,
+ LauncherAppState.ACTION_FORCE_ROLOAD ->
+ // If we have changed locale we need to clear out the labels in all apps/workspace.
+ forceReload()
+ DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED ->
+ enqueueModelUpdateTask(ReloadStringCacheTask(this.modelDelegate))
}
}
@@ -193,43 +182,43 @@
* @see UserCache.addUserEventListener
*/
fun onUserEvent(user: UserHandle, action: String) {
- if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE == action && mShouldReloadWorkProfile) {
- mShouldReloadWorkProfile = false
- forceReload()
- } else if (
- Intent.ACTION_MANAGED_PROFILE_AVAILABLE == action ||
- Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE == action
- ) {
- mShouldReloadWorkProfile = false
- enqueueModelUpdateTask(
- PackageUpdatedTask(PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)
- )
- } else if (
- UserCache.ACTION_PROFILE_LOCKED == action || UserCache.ACTION_PROFILE_UNLOCKED == action
- ) {
- enqueueModelUpdateTask(
- UserLockStateChangedTask(user, UserCache.ACTION_PROFILE_UNLOCKED == action)
- )
- } else if (
- UserCache.ACTION_PROFILE_ADDED == action || UserCache.ACTION_PROFILE_REMOVED == action
- ) {
- forceReload()
- } else if (
- UserCache.ACTION_PROFILE_AVAILABLE == action ||
- UserCache.ACTION_PROFILE_UNAVAILABLE == action
- ) {
- /*
- * This broadcast is only available when android.os.Flags.allowPrivateProfile() is set.
- * For Work-profile this broadcast will be sent in addition to
- * ACTION_MANAGED_PROFILE_AVAILABLE/UNAVAILABLE.
- * So effectively, this if block only handles the non-work profile case.
- */
- enqueueModelUpdateTask(
- PackageUpdatedTask(PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)
- )
- }
- if (Intent.ACTION_MANAGED_PROFILE_REMOVED == action) {
- LauncherPrefs.get(mApp.context).put(LauncherPrefs.WORK_EDU_STEP, 0)
+ when (action) {
+ Intent.ACTION_MANAGED_PROFILE_AVAILABLE -> {
+ if (mShouldReloadWorkProfile) {
+ forceReload()
+ } else {
+ enqueueModelUpdateTask(
+ PackageUpdatedTask(PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)
+ )
+ }
+ mShouldReloadWorkProfile = false
+ }
+ Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE -> {
+ mShouldReloadWorkProfile = false
+ enqueueModelUpdateTask(
+ PackageUpdatedTask(PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)
+ )
+ }
+ UserCache.ACTION_PROFILE_LOCKED ->
+ enqueueModelUpdateTask(UserLockStateChangedTask(user, false))
+ UserCache.ACTION_PROFILE_UNLOCKED ->
+ enqueueModelUpdateTask(UserLockStateChangedTask(user, true))
+ Intent.ACTION_MANAGED_PROFILE_REMOVED -> {
+ LauncherPrefs.get(mApp.context).put(LauncherPrefs.WORK_EDU_STEP, 0)
+ forceReload()
+ }
+ UserCache.ACTION_PROFILE_ADDED,
+ UserCache.ACTION_PROFILE_REMOVED -> forceReload()
+ UserCache.ACTION_PROFILE_AVAILABLE,
+ UserCache.ACTION_PROFILE_UNAVAILABLE -> {
+ // This broadcast is only available when android.os.Flags.allowPrivateProfile() is
+ // set. For Work-profile this broadcast will be sent in addition to
+ // ACTION_MANAGED_PROFILE_AVAILABLE/UNAVAILABLE. So effectively, this if block only
+ // handles the non-work profile case.
+ enqueueModelUpdateTask(
+ PackageUpdatedTask(PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)
+ )
+ }
}
}
@@ -243,12 +232,7 @@
stopLoader()
mModelLoaded = false
}
-
- // Start the loader if launcher is already running, otherwise the loader will run,
- // the next time launcher starts
- if (hasCallbacks()) {
- startLoader()
- }
+ rebindCallbacks()
}
/** Rebinds all existing callbacks with already loaded model */
@@ -325,7 +309,6 @@
}
return true
} else {
- stopLoader()
mLoaderTask =
LoaderTask(
mApp,
@@ -375,83 +358,6 @@
MODEL_EXECUTOR.post { callback.accept(if (isModelLoaded()) mBgDataModel else null) }
}
- override fun onInstallSessionCreated(sessionInfo: PackageInstallInfo) {
- if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
- enqueueModelUpdateTask { taskController, _, apps ->
- apps.addPromiseApp(mApp.context, sessionInfo)
- taskController.bindApplicationsIfNeeded()
- }
- }
- }
-
- override fun onSessionFailure(packageName: String, user: UserHandle) {
- enqueueModelUpdateTask { taskController, dataModel, apps ->
- val iconCache = mApp.iconCache
- val removedIds = IntSet()
- val archivedWorkspaceItemsToCacheRefresh = HashSet<WorkspaceItemInfo>()
- val isAppArchived = ApplicationInfoWrapper(mApp.context, packageName, user).isArchived()
- synchronized(dataModel) {
- if (isAppArchived) {
- // Remove package icon cache entry for archived app in case of a session
- // failure.
- mApp.iconCache.remove(
- ComponentName(packageName, packageName + BaseIconCache.EMPTY_CLASS_NAME),
- user,
- )
- }
- for (info in dataModel.itemsIdMap) {
- if (
- (info is WorkspaceItemInfo && info.hasPromiseIconUi()) &&
- user == info.user &&
- info.intent != null
- ) {
- if (TextUtils.equals(packageName, info.intent!!.getPackage())) {
- removedIds.add(info.id)
- }
- if (info.isArchived()) {
- // Refresh icons on the workspace for archived apps.
- iconCache.getTitleAndIcon(info, info.usingLowResIcon())
- archivedWorkspaceItemsToCacheRefresh.add(info)
- }
- }
- }
- if (isAppArchived) {
- apps.updateIconsAndLabels(hashSetOf(packageName), user)
- }
- }
-
- if (!removedIds.isEmpty && !isAppArchived) {
- taskController.deleteAndBindComponentsRemoved(
- ItemInfoMatcher.ofItemIds(removedIds),
- "removed because install session failed",
- )
- }
- if (archivedWorkspaceItemsToCacheRefresh.isNotEmpty()) {
- taskController.bindUpdatedWorkspaceItems(
- archivedWorkspaceItemsToCacheRefresh.stream().toList()
- )
- }
- if (isAppArchived) {
- taskController.bindApplicationsIfNeeded()
- }
- }
- }
-
- override fun onPackageStateChanged(installInfo: PackageInstallInfo) {
- enqueueModelUpdateTask(PackageInstallStateChangedTask(installInfo))
- }
-
- /** Updates the icons and label of all pending icons for the provided package name. */
- override fun onUpdateSessionDisplay(key: PackageUserKey, info: PackageInstaller.SessionInfo) {
- mApp.iconCache.updateSessionCache(key, info)
-
- val packages = HashSet<String>()
- packages.add(key.mPackageName)
- enqueueModelUpdateTask(
- CacheDataUpdatedTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, key.mUser, packages)
- )
- }
-
inner class LoaderTransaction(task: LoaderTask) : AutoCloseable {
private var mTask: LoaderTask? = null
@@ -545,19 +451,11 @@
}
fun updateAndBindWorkspaceItem(si: WorkspaceItemInfo, info: ShortcutInfo) {
- updateAndBindWorkspaceItem {
- si.updateFromDeepShortcutInfo(info, mApp.context)
- mApp.iconCache.getShortcutIcon(si, info)
- si
- }
- }
-
- /** Utility method to update a shortcut on the background thread. */
- private fun updateAndBindWorkspaceItem(itemProvider: Supplier<WorkspaceItemInfo>) {
enqueueModelUpdateTask { taskController, _, _ ->
- val info = itemProvider.get()
- taskController.getModelWriter().updateItemInDatabase(info)
- taskController.bindUpdatedWorkspaceItems(listOf(info))
+ si.updateFromDeepShortcutInfo(info, context)
+ iconCache.getShortcutIcon(si, info)
+ taskController.getModelWriter().updateItemInDatabase(si)
+ taskController.bindUpdatedWorkspaceItems(listOf(si))
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 4e1e950..e705d94 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -346,17 +346,12 @@
(LetterListTextView) LayoutInflater.from(context).inflate(
R.layout.fast_scroller_letter_list_text_view, mLetterList, false);
int viewId = View.generateViewId();
- textView.setId(viewId);
+ textView.apply(sectionInfo /* FastScrollSectionInfo */, viewId /* viewId */);
sectionInfo.setId(viewId);
- textView.setText(sectionInfo.sectionName);
if (i == fastScrollSections.size() - 1) {
// The last section info is just a duplicate so that user can scroll to the bottom.
textView.setVisibility(INVISIBLE);
}
- ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
- MATCH_CONSTRAINT, WRAP_CONTENT);
- lp.dimensionRatio = "v,1:1";
- textView.setLayoutParams(lp);
textViews.add(textView);
mLetterList.addView(textView);
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 8e44d65..709b52a 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -106,6 +106,7 @@
// The of ordered component names as a result of a search query
private final ArrayList<AdapterItem> mSearchResults = new ArrayList<>();
private final SpannableString mPrivateProfileAppScrollerBadge;
+ private final SpannableString mPrivateProfileDividerBadge;
private BaseAllAppsAdapter<T> mAdapter;
private AppInfoComparator mAppNameComparator;
private int mNumAppsPerRowAllApps;
@@ -124,9 +125,14 @@
mAllAppsStore.addUpdateListener(this);
}
mPrivateProfileAppScrollerBadge = new SpannableString(" ");
- mPrivateProfileAppScrollerBadge.setSpan(new ImageSpan(context,
+ mPrivateProfileAppScrollerBadge.setSpan(new ImageSpan(context, Flags.letterFastScroller()
+ ? R.drawable.ic_private_profile_letter_list_fast_scroller_badge :
R.drawable.ic_private_profile_app_scroller_badge, ImageSpan.ALIGN_CENTER),
0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ mPrivateProfileDividerBadge = new SpannableString(" ");
+ mPrivateProfileDividerBadge.setSpan(new ImageSpan(context,
+ R.drawable.ic_private_profile_divider_badge, ImageSpan.ALIGN_CENTER),
+ 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
/** Set the number of apps per row when device profile changes. */
@@ -404,6 +410,11 @@
// Add system apps separator.
if (Flags.privateSpaceSysAppsSeparation()) {
position = mPrivateProviderManager.addSystemAppsDivider(mAdapterItems);
+ if (Flags.letterFastScroller()) {
+ FastScrollSectionInfo sectionInfo =
+ new FastScrollSectionInfo(mPrivateProfileDividerBadge, position);
+ mFastScrollerSections.add(sectionInfo);
+ }
}
// Add system apps.
position = addAppsWithSections(split.get(false), position);
@@ -437,8 +448,11 @@
Log.d(TAG, "addAppsWithSections: adding sectionName: " + sectionName
+ " with appInfoTitle: " + info.title);
lastSectionName = sectionName;
- mFastScrollerSections.add(new FastScrollSectionInfo(hasPrivateApps ?
- mPrivateProfileAppScrollerBadge : sectionName, position));
+ boolean usePrivateAppScrollerBadge = !Flags.letterFastScroller() && hasPrivateApps;
+ FastScrollSectionInfo sectionInfo = new FastScrollSectionInfo(
+ usePrivateAppScrollerBadge ?
+ mPrivateProfileAppScrollerBadge : sectionName, position);
+ mFastScrollerSections.add(sectionInfo);
}
position++;
}
diff --git a/src/com/android/launcher3/allapps/LetterListTextView.java b/src/com/android/launcher3/allapps/LetterListTextView.java
index 9326d79..433a7f2 100644
--- a/src/com/android/launcher3/allapps/LetterListTextView.java
+++ b/src/com/android/launcher3/allapps/LetterListTextView.java
@@ -16,6 +16,9 @@
package com.android.launcher3.allapps;
+import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
+import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
@@ -23,6 +26,7 @@
import android.util.AttributeSet;
import android.widget.TextView;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.R;
@@ -71,6 +75,20 @@
}
/**
+ * Applies a viewId to the letter list text view and sets the background and text based on the
+ * sectionInfo.
+ */
+ public void apply(AlphabeticalAppsList.FastScrollSectionInfo fastScrollSectionInfo,
+ int viewId) {
+ setId(viewId);
+ setText(fastScrollSectionInfo.sectionName);
+ ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
+ MATCH_CONSTRAINT, WRAP_CONTENT);
+ lp.dimensionRatio = "v,1:1";
+ setLayoutParams(lp);
+ }
+
+ /**
* Animates the letter list text view based on the current finger position.
*
* @param currentFingerY The Y position of where the finger is placed on the fastScroller in
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 6a40121..9c36dc2 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -474,7 +474,8 @@
break;
}
// Make the private space apps gone to "collapse".
- if (mFloatingMaskView == null && isPrivateSpaceItem(currentItem)) {
+ if ((mFloatingMaskView == null && isPrivateSpaceItem(currentItem)) ||
+ currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER) {
RecyclerView.ViewHolder viewHolder =
allAppsRecyclerView.findViewHolderForAdapterPosition(i);
if (viewHolder != null) {
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index de3bb9e..7e3e392 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -118,8 +118,14 @@
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
- Log.i(TAG, "User tapped ime search button");
+ if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO || (
+ actionId == EditorInfo.IME_NULL && event != null
+ && event.getAction() == KeyEvent.ACTION_DOWN)) {
+ if (actionId == EditorInfo.IME_NULL) {
+ Log.i(TAG, "User pressed ENTER key");
+ } else {
+ Log.i(TAG, "User tapped ime search button");
+ }
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem();
}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 088277b..4da7c27 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -20,8 +20,10 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.PluginManagerWrapper;
import com.android.launcher3.util.ScreenOnTracker;
import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
import dagger.BindsInstance;
@@ -38,6 +40,8 @@
InstallSessionHelper getInstallSessionHelper();
ScreenOnTracker getScreenOnTracker();
SettingsCache getSettingsCache();
+ CustomWidgetManager getCustomWidgetManager();
+ PluginManagerWrapper getPluginManagerWrapper();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index a3cfe5c..25de479 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -281,7 +281,7 @@
new PinShortcutRequestActivityInfo(mRequest, this);
mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
applyWidgetItemAsync(
- () -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
+ () -> new WidgetItem(shortcutInfo, mApp.getIconCache()));
return new PackageItemInfo(mRequest.getShortcutInfo().getPackage(),
mRequest.getShortcutInfo().getUserHandle());
}
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index cc5e890..a6a50d7 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -30,7 +30,6 @@
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.PinItemRequest;
-import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -40,7 +39,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
@@ -82,12 +81,12 @@
}
@Override
- public CharSequence getLabel(PackageManager pm) {
+ public CharSequence getLabel() {
return mInfo.getShortLabel();
}
@Override
- public Drawable getFullResIcon(IconCache cache) {
+ public Drawable getFullResIcon(BaseIconCache cache) {
Drawable d = mContext.getSystemService(LauncherApps.class)
.getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi);
if (d == null) {
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index b6fe66a..e7c4024 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -51,8 +51,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.BaseIconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.icons.cache.CachedObjectCachingLogic;
-import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
@@ -92,9 +92,6 @@
private final Predicate<ItemInfoWithIcon> mIsUsingFallbackOrNonDefaultIconCheck = w ->
w.bitmap != null && (w.bitmap.isNullOrLowRes() || !isDefaultIcon(w.bitmap, w.user));
- private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
- private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
-
private final LauncherApps mLauncherApps;
private final UserCache mUserManager;
private final InstantAppResolver mInstantAppResolver;
@@ -108,8 +105,6 @@
IconProvider iconProvider) {
super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */, iconProvider);
- mComponentWithLabelCachingLogic = new CachedObjectCachingLogic(context);
- mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.INSTANCE;
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mUserManager = UserCache.INSTANCE.get(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
@@ -143,7 +138,7 @@
removeIconsForPkg(packageName, user);
long userSerial = mUserManager.getSerialNumberForUser(user);
for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
- addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, userSerial);
+ addIconToDBAndMemCache(app, LauncherActivityCachingLogic.INSTANCE, userSerial);
}
}
@@ -211,7 +206,7 @@
*/
public synchronized void updateTitleAndIcon(AppInfo application) {
CacheEntry entry = cacheLocked(application.componentName,
- application.user, () -> null, mLauncherActivityInfoCachingLogic,
+ application.user, () -> null, LauncherActivityCachingLogic.INSTANCE,
application.usingLowResIcon() ? LookupFlag.USE_LOW_RES : LookupFlag.DEFAULT);
if (entry.bitmap != null || !isDefaultIcon(entry.bitmap, application.user)) {
applyCacheEntry(entry, application);
@@ -326,9 +321,9 @@
/**
* Loads and returns the icon for the provided object without adding it to memCache
*/
- public synchronized String getTitleNoCache(ComponentWithLabel info) {
+ public synchronized String getTitleNoCache(CachedObject info) {
CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
- mComponentWithLabelCachingLogic,
+ CachedObjectCachingLogic.INSTANCE,
LookupFlag.USE_LOW_RES | LookupFlag.SKIP_ADD_TO_MEM_CACHE);
return Utilities.trim(entry.title);
}
@@ -344,7 +339,7 @@
if (usePkgIcon) lookupFlags |= LookupFlag.USE_PACKAGE_ICON;
if (useLowResIcon) lookupFlags |= LookupFlag.USE_LOW_RES;
CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
- activityInfoProvider, mLauncherActivityInfoCachingLogic, lookupFlags);
+ activityInfoProvider, LauncherActivityCachingLogic.INSTANCE, lookupFlags);
applyCacheEntry(entry, infoInOut);
}
@@ -445,7 +440,7 @@
cn,
/* user = */ sectionKey.first,
() -> duplicateIconRequests.get(0).launcherActivityInfo,
- mLauncherActivityInfoCachingLogic,
+ LauncherActivityCachingLogic.INSTANCE,
sectionKey.second ? LookupFlag.USE_LOW_RES : LookupFlag.DEFAULT,
c);
@@ -494,7 +489,7 @@
loadFallbackIcon(
lai,
entry,
- mLauncherActivityInfoCachingLogic,
+ LauncherActivityCachingLogic.INSTANCE,
/* usePackageIcon= */ false,
/* usePackageTitle= */ loadFallbackTitle,
cn,
@@ -504,7 +499,7 @@
loadFallbackTitle(
lai,
entry,
- mLauncherActivityInfoCachingLogic,
+ LauncherActivityCachingLogic.INSTANCE,
sectionKey.first);
}
diff --git a/src/com/android/launcher3/icons/Legacy.kt b/src/com/android/launcher3/icons/Legacy.kt
deleted file mode 100644
index 3bf3bb2..0000000
--- a/src/com/android/launcher3/icons/Legacy.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2024 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.icons
-
-import com.android.launcher3.icons.cache.CachedObject
-
-/**
- * This files contains some definitions used during refactoring to avoid breaking changes.
- *
- * TODO(b/366237794) remove this file once refactoring is complete
- */
-
-/** Temporary interface to allow easier refactoring */
-interface ComponentWithLabel : CachedObject<IconCache>
-
-/** Temporary interface to allow easier refactoring */
-interface ComponentWithLabelAndIcon : ComponentWithLabel
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index fbd24d8..e5cd76a 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -221,6 +221,9 @@
@UiEvent(doc = "User tapped on desktop icon on a task menu.")
LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP(1706),
+ @UiEvent(doc = "Use tapped on external display icon on a task menu,")
+ LAUNCHER_SYSTEM_SHORTCUT_EXTERNAL_DISPLAY_TAP(1957),
+
@UiEvent(doc = "User tapped on pause app system shortcut.")
LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP(521),
@@ -798,6 +801,44 @@
@UiEvent(doc = "User long pressed on the taskbar IME switcher button")
LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_LONGPRESS(1798),
+ @UiEvent(doc = "Failed to launch assistant due to Google assistant not available")
+ LAUNCHER_LAUNCH_ASSISTANT_FAILED_NOT_AVAILABLE(1465),
+
+ @UiEvent(doc = "Failed to launch assistant due to service error")
+ LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR(1466),
+
+ @UiEvent(doc = "User launched assistant by long-pressing nav handle")
+ LAUNCHER_LAUNCH_ASSISTANT_SUCCESSFUL_NAV_HANDLE(1467),
+
+ @UiEvent(doc = "Failed to launch due to Contextual Search not available")
+ LAUNCHER_LAUNCH_OMNI_FAILED_NOT_AVAILABLE(1471),
+
+ @UiEvent(doc = "Failed to launch due to Contextual Search setting disabled")
+ LAUNCHER_LAUNCH_OMNI_FAILED_SETTING_DISABLED(1632),
+
+ @UiEvent(doc = "User launched Contextual Search by long-pressing home in 3-button mode")
+ LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_HOME(1481),
+
+ @UiEvent(doc = "User launched Contextual Search by using accessibility System Action")
+ LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION(1492),
+
+ @UiEvent(doc = "User launched Contextual Search by long pressing the meta key")
+ LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_META(1606),
+
+ @UiEvent(doc = "Contextual Search invocation was attempted over the notification shade")
+ LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_NOTIFICATION_SHADE(1485),
+
+ @UiEvent(doc = "The Contextual Search all entrypoints toggle value in Settings")
+ LAUNCHER_SETTINGS_OMNI_ALL_ENTRYPOINTS_TOGGLE_VALUE(1633),
+
+ @UiEvent(doc = "Contextual Search invocation was attempted over the keyguard")
+ LAUNCHER_LAUNCH_OMNI_ATTEMPTED_OVER_KEYGUARD(1501),
+
+ @UiEvent(doc = "Contextual Search invocation was attempted while splitscreen is active")
+ LAUNCHER_LAUNCH_OMNI_ATTEMPTED_SPLITSCREEN(1505),
+
+ @UiEvent(doc = "User long press nav handle and a long press runnable was created.")
+ LAUNCHER_OMNI_GET_LONG_PRESS_RUNNABLE(1545),
// ADD MORE
;
@@ -828,6 +869,10 @@
@UiEvent(doc = "The duration of asynchronous loading workspace")
LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC(1367),
+
+ @UiEvent(doc = "Time passed between Contextual Search runnable creation and execution. This"
+ + " ensures that Recent animations have finished before Contextual Search starts.")
+ LAUNCHER_LATENCY_OMNI_RUNNABLE(1546),
;
private final int mId;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index dff5463..09d1146 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -72,8 +72,8 @@
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.icons.CacheableShortcutCachingLogic;
import com.android.launcher3.icons.CacheableShortcutInfo;
-import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.icons.cache.CachedObjectCachingLogic;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
@@ -335,8 +335,7 @@
verifyNotStopped();
// fourth step
- List<ComponentWithLabelAndIcon> allWidgetsList =
- mBgDataModel.widgetsModel.update(mApp, null);
+ List<CachedObject> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
logASplit("load widgets");
verifyNotStopped();
@@ -364,7 +363,7 @@
}
updateHandler.updateIcons(allWidgetsList,
- new CachedObjectCachingLogic(mApp.getContext()),
+ CachedObjectCachingLogic.INSTANCE,
mApp.getModel()::onWidgetLabelsUpdated);
logASplit("save widgets in icon cache");
diff --git a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
index 2ee5b80..7ba2dad 100644
--- a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
+++ b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
@@ -17,10 +17,12 @@
package com.android.launcher3.model
import android.content.pm.LauncherApps
+import android.content.pm.PackageInstaller.SessionInfo
import android.content.pm.ShortcutInfo
import android.os.UserHandle
import android.text.TextUtils
import com.android.launcher3.LauncherModel.ModelUpdateTask
+import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.PackageUpdatedTask.OP_ADD
import com.android.launcher3.model.PackageUpdatedTask.OP_REMOVE
@@ -28,6 +30,9 @@
import com.android.launcher3.model.PackageUpdatedTask.OP_UNAVAILABLE
import com.android.launcher3.model.PackageUpdatedTask.OP_UNSUSPEND
import com.android.launcher3.model.PackageUpdatedTask.OP_UPDATE
+import com.android.launcher3.pm.InstallSessionTracker
+import com.android.launcher3.pm.PackageInstallInfo
+import com.android.launcher3.util.PackageUserKey
import java.util.function.Consumer
/**
@@ -35,7 +40,7 @@
* model tasks
*/
class ModelLauncherCallbacks(private var taskExecutor: Consumer<ModelUpdateTask>) :
- LauncherApps.Callback() {
+ LauncherApps.Callback(), InstallSessionTracker.Callback {
override fun onPackageAdded(packageName: String, user: UserHandle) {
FileLog.d(TAG, "onPackageAdded triggered for packageName=$packageName, user=$user")
@@ -49,7 +54,7 @@
override fun onPackageLoadingProgressChanged(
packageName: String,
user: UserHandle,
- progress: Float
+ progress: Float,
) {
taskExecutor.accept(PackageIncrementalDownloadUpdatedTask(packageName, user, progress))
}
@@ -62,7 +67,7 @@
override fun onPackagesAvailable(
vararg packageNames: String,
user: UserHandle,
- replacing: Boolean
+ replacing: Boolean,
) {
taskExecutor.accept(PackageUpdatedTask(OP_UPDATE, user, *packageNames))
}
@@ -74,7 +79,7 @@
override fun onPackagesUnavailable(
packageNames: Array<String>,
user: UserHandle,
- replacing: Boolean
+ replacing: Boolean,
) {
if (!replacing) {
taskExecutor.accept(PackageUpdatedTask(OP_UNAVAILABLE, user, *packageNames))
@@ -88,7 +93,7 @@
override fun onShortcutsChanged(
packageName: String,
shortcuts: MutableList<ShortcutInfo>,
- user: UserHandle
+ user: UserHandle,
) {
taskExecutor.accept(ShortcutsChangedTask(packageName, shortcuts, user, true))
}
@@ -98,6 +103,37 @@
taskExecutor.accept(PackageUpdatedTask(OP_REMOVE, user, *packages.toTypedArray()))
}
+ override fun onSessionFailure(packageName: String, user: UserHandle) {
+ taskExecutor.accept(SessionFailureTask(packageName, user))
+ }
+
+ override fun onPackageStateChanged(installInfo: PackageInstallInfo) {
+ taskExecutor.accept(PackageInstallStateChangedTask(installInfo))
+ }
+
+ override fun onUpdateSessionDisplay(key: PackageUserKey, info: SessionInfo) {
+ /** Updates the icons and label of all pending icons for the provided package name. */
+ taskExecutor.accept { controller, _, _ ->
+ controller.app.iconCache.updateSessionCache(key, info)
+ }
+ taskExecutor.accept(
+ CacheDataUpdatedTask(
+ CacheDataUpdatedTask.OP_SESSION_UPDATE,
+ key.mUser,
+ hashSetOf(key.mPackageName),
+ )
+ )
+ }
+
+ override fun onInstallSessionCreated(sessionInfo: PackageInstallInfo) {
+ if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
+ taskExecutor.accept { taskController, _, apps ->
+ apps.addPromiseApp(taskController.app.context, sessionInfo)
+ taskController.bindApplicationsIfNeeded()
+ }
+ }
+ }
+
companion object {
private const val TAG = "LauncherAppsCallbackImpl"
}
diff --git a/src/com/android/launcher3/model/SessionFailureTask.kt b/src/com/android/launcher3/model/SessionFailureTask.kt
new file mode 100644
index 0000000..0d006fa
--- /dev/null
+++ b/src/com/android/launcher3/model/SessionFailureTask.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model
+
+import android.content.ComponentName
+import android.os.UserHandle
+import android.text.TextUtils
+import com.android.launcher3.LauncherModel.ModelUpdateTask
+import com.android.launcher3.icons.cache.BaseIconCache
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.ApplicationInfoWrapper
+import com.android.launcher3.util.ItemInfoMatcher
+
+/** Model task run when there is a package install session failure */
+class SessionFailureTask(val packageName: String, val user: UserHandle) : ModelUpdateTask {
+
+ override fun execute(
+ taskController: ModelTaskController,
+ dataModel: BgDataModel,
+ apps: AllAppsList,
+ ) {
+ val iconCache = taskController.app.iconCache
+ val isAppArchived =
+ ApplicationInfoWrapper(taskController.app.context, packageName, user).isArchived()
+ synchronized(dataModel) {
+ if (isAppArchived) {
+ val updatedItems = mutableListOf<WorkspaceItemInfo>()
+ // Remove package icon cache entry for archived app in case of a session
+ // failure.
+ iconCache.remove(
+ ComponentName(packageName, packageName + BaseIconCache.EMPTY_CLASS_NAME),
+ user,
+ )
+ for (info in dataModel.itemsIdMap) {
+ if (info is WorkspaceItemInfo && info.isArchived && user == info.user) {
+ // Refresh icons on the workspace for archived apps.
+ iconCache.getTitleAndIcon(info, info.usingLowResIcon())
+ updatedItems.add(info)
+ }
+ }
+
+ if (updatedItems.isNotEmpty()) {
+ taskController.bindUpdatedWorkspaceItems(updatedItems)
+ }
+ apps.updateIconsAndLabels(hashSetOf(packageName), user)
+ taskController.bindApplicationsIfNeeded()
+ } else {
+ val removedItems =
+ dataModel.itemsIdMap.filter { info ->
+ (info is WorkspaceItemInfo && info.hasPromiseIconUi()) &&
+ user == info.user &&
+ TextUtils.equals(packageName, info.intent.getPackage())
+ }
+ if (removedItems.isNotEmpty()) {
+ taskController.deleteAndBindComponentsRemoved(
+ ItemInfoMatcher.ofItems(removedItems),
+ "removed because install session failed",
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index ac9f2d6..e757a68 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -7,7 +7,6 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.util.SparseArray;
import android.widget.RemoteViews;
@@ -75,10 +74,10 @@
this(info, idp, iconCache, context, new WidgetManagerHelper(context));
}
- public WidgetItem(ShortcutConfigActivityInfo info, IconCache iconCache, PackageManager pm) {
+ public WidgetItem(ShortcutConfigActivityInfo info, IconCache iconCache) {
super(info.getComponent(), info.getUser());
label = info.isPersistable() ? iconCache.getTitleNoCache(info) :
- Utilities.trim(info.getLabel(pm));
+ Utilities.trim(info.getLabel());
description = null;
widgetInfo = null;
activityInfo = info;
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index c949ce6..b450f46 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -14,7 +14,6 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -27,8 +26,8 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ComponentKey;
@@ -96,20 +95,18 @@
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public List<ComponentWithLabelAndIcon> update(
+ public List<CachedObject> update(
LauncherAppState app, @Nullable PackageUserKey packageUser) {
if (!WIDGETS_ENABLED) {
- return Collections.emptyList();
+ return new ArrayList<>();
}
Preconditions.assertWorkerThread();
Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
- List<ComponentWithLabelAndIcon> updatedItems = new ArrayList<>();
+ List<CachedObject> updatedItems = new ArrayList<>();
try {
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
- PackageManager pm = app.getContext().getPackageManager();
-
// Widgets
WidgetManagerHelper widgetManager = new WidgetManagerHelper(context);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
@@ -125,7 +122,7 @@
// Shortcuts
for (ShortcutConfigActivityInfo info :
queryList(context, packageUser)) {
- widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
+ widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache()));
updatedItems.add(info);
}
setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
@@ -190,8 +187,7 @@
WidgetItem item = items.get(i);
if (item.user.equals(user)) {
if (item.activityInfo != null) {
- items.set(i, new WidgetItem(item.activityInfo, app.getIconCache(),
- app.getContext().getPackageManager()));
+ items.set(i, new WidgetItem(item.activityInfo, app.getIconCache()));
} else {
items.set(i, new WidgetItem(item.widgetInfo,
app.getInvariantDeviceProfile(), app.getIconCache(),
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index f36f595..afc5117 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -38,13 +38,10 @@
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.DaggerSingletonObject;
-import com.android.launcher3.util.DaggerSingletonTracker;
-import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SafeCloseable;
import java.util.ArrayList;
import java.util.HashMap;
@@ -59,7 +56,7 @@
*/
@SuppressWarnings("NewApi")
@LauncherAppSingleton
-public class InstallSessionHelper implements SafeCloseable {
+public class InstallSessionHelper {
@NonNull
private static final String LOG = "InstallSessionHelper";
@@ -91,17 +88,12 @@
private IntSet mPromiseIconIds;
@Inject
- public InstallSessionHelper(@NonNull @ApplicationContext final Context context,
- DaggerSingletonTracker tracker) {
+ public InstallSessionHelper(@NonNull @ApplicationContext final Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
- ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
- @Override
- public void close() { }
-
@WorkerThread
@NonNull
private IntSet getPromiseIconIds() {
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index 3064abf..409174e 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -29,7 +29,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Process;
@@ -41,8 +40,8 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.icons.ComponentWithLabelAndIcon;
-import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.BaseIconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.PackageUserKey;
@@ -54,7 +53,7 @@
/**
* Wrapper class for representing a shortcut configure activity.
*/
-public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAndIcon {
+public abstract class ShortcutConfigActivityInfo implements CachedObject {
private static final String TAG = "SCActivityInfo";
@@ -91,7 +90,7 @@
}
@Override
- public abstract Drawable getFullResIcon(IconCache cache);
+ public abstract Drawable getFullResIcon(BaseIconCache cache);
/**
* Return a WorkspaceItemInfo, if it can be created directly on drop, without requiring any
@@ -125,7 +124,7 @@
}
/**
- * Returns true if various properties ({@link #getLabel(PackageManager)},
+ * Returns true if various properties ({@link #getLabel()},
* {@link #getFullResIcon}) can be safely persisted.
*/
public boolean isPersistable() {
@@ -144,12 +143,12 @@
}
@Override
- public CharSequence getLabel(PackageManager pm) {
+ public CharSequence getLabel() {
return mInfo.getLabel();
}
@Override
- public Drawable getFullResIcon(IconCache cache) {
+ public Drawable getFullResIcon(BaseIconCache cache) {
return cache.getFullResIcon(mInfo.getActivityInfo());
}
diff --git a/src/com/android/launcher3/util/DaggerSingletonObject.java b/src/com/android/launcher3/util/DaggerSingletonObject.java
index b8cf2ae..febe6af 100644
--- a/src/com/android/launcher3/util/DaggerSingletonObject.java
+++ b/src/com/android/launcher3/util/DaggerSingletonObject.java
@@ -29,7 +29,7 @@
* We should delete this class at the end and use @Inject to get dagger provided singletons.
*/
-public class DaggerSingletonObject<T extends SafeCloseable> {
+public class DaggerSingletonObject<T> {
private final Function<LauncherAppComponent, T> mFunction;
public DaggerSingletonObject(Function<LauncherAppComponent, T> function) {
diff --git a/src/com/android/launcher3/util/PluginManagerWrapper.java b/src/com/android/launcher3/util/PluginManagerWrapper.java
index b27aa12..5b28570 100644
--- a/src/com/android/launcher3/util/PluginManagerWrapper.java
+++ b/src/com/android/launcher3/util/PluginManagerWrapper.java
@@ -15,32 +15,39 @@
*/
package com.android.launcher3.util;
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import androidx.annotation.AnyThread;
-import com.android.launcher3.R;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import java.io.PrintWriter;
-public class PluginManagerWrapper implements ResourceBasedOverride, SafeCloseable {
+import javax.inject.Inject;
- public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE =
- forOverride(PluginManagerWrapper.class, R.string.plugin_manager_wrapper_class);
+@LauncherAppSingleton
+public class PluginManagerWrapper{
+ public static final DaggerSingletonObject<PluginManagerWrapper> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getPluginManagerWrapper);
+
+ @Inject
+ public PluginManagerWrapper() { }
+
+ @AnyThread
public <T extends Plugin> void addPluginListener(
PluginListener<T> listener, Class<T> pluginClass) {
addPluginListener(listener, pluginClass, false);
}
+ @AnyThread
public <T extends Plugin> void addPluginListener(
PluginListener<T> listener, Class<T> pluginClass, boolean allowMultiple) {
}
+ @AnyThread
public void removePluginListener(PluginListener<? extends Plugin> listener) { }
- @Override
- public void close() { }
-
public void dump(PrintWriter pw) { }
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index 1db3b5a..b877d7a 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -16,8 +16,8 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabelAndIcon;
-import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.BaseIconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
/**
@@ -26,8 +26,7 @@
* (who's implementation is owned by the launcher). This object represents a widget type / class,
* as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
*/
-public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
- implements ComponentWithLabelAndIcon {
+public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo implements CachedObject {
public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
@@ -69,6 +68,8 @@
protected boolean mIsMinSizeFulfilled;
+ private PackageManager mPM;
+
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
AppWidgetProviderInfo info) {
final LauncherAppWidgetProviderInfo launcherInfo;
@@ -97,6 +98,7 @@
}
public void initSpans(Context context, InvariantDeviceProfile idp) {
+ mPM = context.getApplicationContext().getPackageManager();
int minSpanX = 0;
int minSpanY = 0;
int maxSpanX = idp.numColumns;
@@ -104,7 +106,6 @@
int spanX = 0;
int spanY = 0;
-
Point cellSize = new Point();
for (DeviceProfile dp : idp.supportedProfiles) {
dp.getCellSize(cellSize);
@@ -188,8 +189,9 @@
(widgetSize + widgetPadding + cellSpacing) / (cellSize + cellSpacing)));
}
- public String getLabel(PackageManager packageManager) {
- return super.loadLabel(packageManager);
+ @Override
+ public CharSequence getLabel() {
+ return super.loadLabel(mPM);
}
public Point getMinSpans() {
@@ -225,7 +227,7 @@
}
@Override
- public Drawable getFullResIcon(IconCache cache) {
+ public Drawable getFullResIcon(BaseIconCache cache) {
return cache.getFullResIcon(getActivityInfo());
}
diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
index 5ad9222..82a6883 100644
--- a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
@@ -18,7 +18,6 @@
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
@@ -64,7 +63,7 @@
}
@Override
- public String getLabel(PackageManager packageManager) {
+ public CharSequence getLabel() {
return Utilities.trim(label);
}
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index faa5d12..4aeac76 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -33,10 +33,14 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PluginManagerWrapper;
-import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.systemui.plugins.CustomWidgetPlugin;
@@ -50,13 +54,16 @@
import java.util.function.Consumer;
import java.util.stream.Stream;
+import javax.inject.Inject;
+
/**
* CustomWidgetManager handles custom widgets implemented as a plugin.
*/
-public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin>, SafeCloseable {
+@LauncherAppSingleton
+public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> {
- public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE =
- new MainThreadInitializedObject<>(CustomWidgetManager::new);
+ public static final DaggerSingletonObject<CustomWidgetManager> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getCustomWidgetManager);
private static final String TAG = "CustomWidgetManager";
private static final String PLUGIN_PKG = "android";
@@ -66,39 +73,44 @@
private Consumer<PackageUserKey> mWidgetRefreshCallback;
private final @NonNull AppWidgetManager mAppWidgetManager;
- private CustomWidgetManager(Context context) {
- this(context, AppWidgetManager.getInstance(context));
+ @Inject
+ CustomWidgetManager(@ApplicationContext Context context, PluginManagerWrapper pluginManager,
+ DaggerSingletonTracker tracker) {
+ this(context, pluginManager, AppWidgetManager.getInstance(context), tracker);
}
@VisibleForTesting
- CustomWidgetManager(Context context, @NonNull AppWidgetManager widgetManager) {
+ CustomWidgetManager(@ApplicationContext Context context,
+ PluginManagerWrapper pluginManager,
+ @NonNull AppWidgetManager widgetManager,
+ DaggerSingletonTracker tracker) {
mContext = context;
mAppWidgetManager = widgetManager;
mPlugins = new HashMap<>();
mCustomWidgets = new ArrayList<>();
- PluginManagerWrapper.INSTANCE.get(context)
- .addPluginListener(this, CustomWidgetPlugin.class, true);
- if (enableSmartspaceAsAWidget()) {
- for (String s: context.getResources()
- .getStringArray(R.array.custom_widget_providers)) {
- try {
- Class<?> cls = Class.forName(s);
- CustomWidgetPlugin plugin = (CustomWidgetPlugin)
- cls.getDeclaredConstructor(Context.class).newInstance(context);
- onPluginConnected(plugin, context);
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
- | ClassCastException | NoSuchMethodException
- | InvocationTargetException e) {
- Log.e(TAG, "Exception found when trying to add custom widgets: " + e);
+ pluginManager.addPluginListener(this, CustomWidgetPlugin.class, true);
+
+ ExecutorUtil.executeSyncOnMainOrFail(() -> {
+ if (enableSmartspaceAsAWidget()) {
+ for (String s: context.getResources()
+ .getStringArray(R.array.custom_widget_providers)) {
+ try {
+ Class<?> cls = Class.forName(s);
+ CustomWidgetPlugin plugin = (CustomWidgetPlugin)
+ cls.getDeclaredConstructor(Context.class).newInstance(context);
+ onPluginConnected(plugin, context);
+ } catch (ClassNotFoundException | InstantiationException
+ | IllegalAccessException
+ | ClassCastException | NoSuchMethodException
+ | InvocationTargetException e) {
+ Log.e(TAG, "Exception found when trying to add custom widgets: " + e);
+ }
}
}
- }
- }
- @Override
- public void close() {
- PluginManagerWrapper.INSTANCE.get(mContext).removePluginListener(this);
+ tracker.addCloseable(() -> pluginManager.removePluginListener(this));
+ });
}
@Override
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 4e9143e..825b52b 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -167,7 +167,6 @@
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String ICON_MISSING = "b/282963545";
- public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
index 058ac05..946bbc5 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefs.kt
@@ -17,6 +17,7 @@
package com.android.launcher3
import android.content.Context
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
/** Emulates Launcher preferences for a test environment. */
class FakeLauncherPrefs(private val context: Context) : LauncherPrefs() {
@@ -69,5 +70,8 @@
override fun close() = Unit
- private fun notifyChange(key: String) = listeners.forEach { it.onPrefChanged(key) }
+ private fun notifyChange(key: String) {
+ // Mimics SharedPreferencesImpl#notifyListeners main thread dispatching.
+ MAIN_EXECUTOR.execute { listeners.forEach { it.onPrefChanged(key) } }
+ }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
index b8e347c..2463c93 100644
--- a/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/FakeLauncherPrefsTest.kt
@@ -17,6 +17,7 @@
package com.android.launcher3
import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -107,7 +108,7 @@
fun testAddListener_changeItemInPrefs_callsListener() {
var changedKey: String? = null
launcherPrefs.addListener({ changedKey = it }, TEST_CONSTANT_ITEM)
- launcherPrefs.put(TEST_CONSTANT_ITEM, true)
+ getInstrumentation().runOnMainSync { launcherPrefs.put(TEST_CONSTANT_ITEM, true) }
assertThat(changedKey).isEqualTo(TEST_CONSTANT_ITEM.sharedPrefKey)
}
@@ -117,7 +118,7 @@
launcherPrefs.put(TEST_CONSTANT_ITEM, true)
launcherPrefs.addListener({ changedKey = it }, TEST_CONSTANT_ITEM)
- launcherPrefs.remove(TEST_CONSTANT_ITEM)
+ getInstrumentation().runOnMainSync { launcherPrefs.remove(TEST_CONSTANT_ITEM) }
assertThat(changedKey).isEqualTo(TEST_CONSTANT_ITEM.sharedPrefKey)
}
@@ -128,7 +129,7 @@
launcherPrefs.addListener(listener, TEST_CONSTANT_ITEM)
launcherPrefs.removeListener(listener)
- launcherPrefs.put(TEST_CONSTANT_ITEM, true)
+ getInstrumentation().runOnMainSync { launcherPrefs.put(TEST_CONSTANT_ITEM, true) }
assertThat(changedKey).isNull()
}
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
index 8e54c94..ed9a080 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
@@ -18,10 +18,8 @@
import android.content.ComponentName
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
import android.database.MatrixCursor
import android.os.Process.myUserHandle
-import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.icons.cache.BaseIconCache
@@ -49,7 +47,7 @@
@Mock private lateinit var baseIconCache: BaseIconCache
private var cursor: MatrixCursor? = null
- private var cachingLogic = CachedObjectCachingLogic<BaseIconCache>(getApplicationContext())
+ private var cachingLogic = CachedObjectCachingLogic
@Before
fun setup() {
@@ -137,14 +135,13 @@
}
}
-class TestCachedObject(val cn: ComponentName, val freshnessId: String) :
- CachedObject<BaseIconCache> {
+class TestCachedObject(val cn: ComponentName, val freshnessId: String) : CachedObject {
override fun getComponent() = cn
override fun getUser() = myUserHandle()
- override fun getLabel(pm: PackageManager?): CharSequence? = null
+ override fun getLabel(): CharSequence? = null
override fun getApplicationInfo(): ApplicationInfo? = null
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
index 4f9b8c7..efe7637 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
@@ -73,9 +73,7 @@
override fun apply(statement: Statement, description: Description): Statement {
return object : ExternalResource() {
- override fun before() {
- base.app = this@SandboxApplication
- }
+ override fun before() = init()
override fun after() = onDestroy()
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfoTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfoTest.kt
index 0a3035a..af2c378 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfoTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfoTest.kt
@@ -17,7 +17,6 @@
package com.android.launcher3.widget.custom
import android.content.ComponentName
-import android.content.pm.PackageManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
@@ -25,7 +24,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
@MediumTest
@RunWith(AndroidJUnit4::class)
@@ -47,7 +45,7 @@
@Test
fun get_label() {
underTest.label = " TEST_LABEL"
- assertEquals(LABEL_NAME, underTest.getLabel(mock(PackageManager::class.java)))
+ assertEquals(LABEL_NAME, underTest.getLabel())
}
companion object {
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomWidgetManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomWidgetManagerTest.kt
index 4b5710d..1c25db9 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomWidgetManagerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/custom/CustomWidgetManagerTest.kt
@@ -23,19 +23,22 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
import com.android.launcher3.util.PluginManagerWrapper
+import com.android.launcher3.util.SafeCloseable
import com.android.launcher3.util.WidgetUtils
import com.android.launcher3.widget.LauncherAppWidgetHostView
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
import com.android.systemui.plugins.CustomWidgetPlugin
-import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
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.Mockito.mock
import org.mockito.MockitoAnnotations
@@ -57,17 +60,14 @@
@Mock private lateinit var pluginManager: PluginManagerWrapper
@Mock private lateinit var mockAppWidgetManager: AppWidgetManager
+ @Mock private lateinit var tracker: DaggerSingletonTracker
+
+ @Captor private lateinit var closableCaptor: ArgumentCaptor<SafeCloseable>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- context.putObject(PluginManagerWrapper.INSTANCE, pluginManager)
- underTest = CustomWidgetManager(context, mockAppWidgetManager)
- }
-
- @After
- fun tearDown() {
- underTest.close()
+ underTest = CustomWidgetManager(context, pluginManager, mockAppWidgetManager, tracker)
}
@Test
@@ -78,7 +78,8 @@
@Test
fun close_widget_manager_should_remove_plugin_listener() {
- underTest.close()
+ verify(tracker).addCloseable(closableCaptor.capture())
+ closableCaptor.allValues.forEach(SafeCloseable::close)
verify(pluginManager).removePluginListener(same(underTest))
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/model/WidgetsListBaseEntriesBuilderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/model/WidgetsListBaseEntriesBuilderTest.kt
index 5df7caa..063ab32 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/model/WidgetsListBaseEntriesBuilderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/model/WidgetsListBaseEntriesBuilderTest.kt
@@ -26,8 +26,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
-import com.android.launcher3.icons.ComponentWithLabel
import com.android.launcher3.icons.IconCache
+import com.android.launcher3.icons.cache.CachedObject
import com.android.launcher3.model.WidgetItem
import com.android.launcher3.model.data.PackageItemInfo
import com.android.launcher3.util.ActivityContextWrapper
@@ -66,11 +66,11 @@
testInvariantProfile = LauncherAppState.getIDP(context)
doAnswer { invocation: InvocationOnMock ->
- val componentWithLabel = invocation.getArgument<Any>(0) as ComponentWithLabel
+ val componentWithLabel = invocation.getArgument<Any>(0) as CachedObject
componentWithLabel.getComponent().shortClassName
}
.`when`(iconCache)
- .getTitleNoCache(any<ComponentWithLabel>())
+ .getTitleNoCache(any<CachedObject>())
underTest = WidgetsListBaseEntriesBuilder(context)
allWidgets =
@@ -79,14 +79,14 @@
packageItemInfoWithTitle(APP_1_PACKAGE_NAME, APP_1_PACKAGE_TITLE) to
listOf(
createWidgetItem(APP_1_PACKAGE_NAME, APP_1_PROVIDER_1_CLASS_NAME),
- createWidgetItem(APP_1_PACKAGE_NAME, APP_1_PROVIDER_2_CLASS_NAME)
+ createWidgetItem(APP_1_PACKAGE_NAME, APP_1_PROVIDER_2_CLASS_NAME),
),
// app 2
packageItemInfoWithTitle(APP_2_PACKAGE_NAME, APP_2_PACKAGE_TITLE) to
listOf(createWidgetItem(APP_2_PACKAGE_NAME, APP_2_PROVIDER_1_CLASS_NAME)),
// app 3
packageItemInfoWithTitle(APP_3_PACKAGE_NAME, APP_3_PACKAGE_TITLE) to
- listOf(createWidgetItem(APP_3_PACKAGE_NAME, APP_3_PROVIDER_1_CLASS_NAME))
+ listOf(createWidgetItem(APP_3_PACKAGE_NAME, APP_3_PROVIDER_1_CLASS_NAME)),
)
}
@@ -96,7 +96,7 @@
listOf(
APP_1_EXPECTED_SECTION_NAME to 2,
APP_2_EXPECTED_SECTION_NAME to 1,
- APP_3_EXPECTED_SECTION_NAME to 1
+ APP_3_EXPECTED_SECTION_NAME to 1,
)
val entries = underTest.build(allWidgets)
@@ -122,7 +122,7 @@
val expectedWidgetsCountBySection =
listOf(
APP_1_EXPECTED_SECTION_NAME to 1, // one widget filtered out
- APP_3_EXPECTED_SECTION_NAME to 1
+ APP_3_EXPECTED_SECTION_NAME to 1,
)
val entries =
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index d4e061a..c9b6d4f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -42,8 +42,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.ActivityContextWrapper;
@@ -87,7 +87,7 @@
mTestProfile.numColumns = 5;
doAnswer(invocation -> {
- ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ CachedObject componentWithLabel = invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index e1cc010..0d9464a 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -45,8 +45,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.ActivityContextWrapper;
@@ -92,7 +92,7 @@
mTestProfile.numColumns = 5;
doAnswer(invocation -> {
- ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ CachedObject componentWithLabel = invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
index 1822639..1da74cb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
@@ -27,8 +27,8 @@
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
-import com.android.launcher3.icons.ComponentWithLabel
import com.android.launcher3.icons.IconCache
+import com.android.launcher3.icons.cache.CachedObject
import com.android.launcher3.model.WidgetItem
import com.android.launcher3.model.data.PackageItemInfo
import com.android.launcher3.util.ActivityContextWrapper
@@ -81,11 +81,11 @@
testInvariantProfile = LauncherAppState.getIDP(context)
doAnswer { invocation: InvocationOnMock ->
- val componentWithLabel = invocation.getArgument<Any>(0) as ComponentWithLabel
+ val componentWithLabel = invocation.getArgument<Any>(0) as CachedObject
componentWithLabel.getComponent().shortClassName
}
.`when`(iconCache)
- .getTitleNoCache(any<ComponentWithLabel>())
+ .getTitleNoCache(any<CachedObject>())
appWidgetItem = createWidgetItem()
}
@@ -113,8 +113,8 @@
listOf(
PendingAddWidgetInfo(
appWidgetItem.widgetInfo,
- LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION
- ),
+ LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION,
+ )
)
underTest.setWidgetRecommendations(recommendations)
@@ -133,8 +133,8 @@
listOf(
PendingAddWidgetInfo(
appWidgetItem.widgetInfo,
- LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION
- ),
+ LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION,
+ )
)
underTest.setWidgetRecommendations(recommendations)
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
index 7552619..6088c8e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
@@ -33,8 +33,8 @@
import androidx.test.filters.SmallTest;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -81,7 +81,7 @@
mTestProfile.numColumns = 5;
doAnswer(invocation -> {
- ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ CachedObject componentWithLabel = invocation.getArgument(0);
return mWidgetsToLabels.get(componentWithLabel.getComponent());
}).when(mIconCache).getTitleNoCache(any());
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt
index e59e211..deec67a 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt
@@ -27,8 +27,8 @@
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION
-import com.android.launcher3.icons.ComponentWithLabel
import com.android.launcher3.icons.IconCache
+import com.android.launcher3.icons.cache.CachedObject
import com.android.launcher3.model.WidgetItem
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.PackageItemInfo
@@ -86,11 +86,11 @@
testInvariantProfile = LauncherAppState.getIDP(context)
doAnswer { invocation: InvocationOnMock ->
- val componentWithLabel = invocation.getArgument<Any>(0) as ComponentWithLabel
+ val componentWithLabel = invocation.getArgument<Any>(0) as CachedObject
componentWithLabel.getComponent().shortClassName
}
.`when`(iconCache)
- .getTitleNoCache(any<ComponentWithLabel>())
+ .getTitleNoCache(any<CachedObject>())
app1PackageItemInfo = packageItemInfoWithTitle(APP_1_PACKAGE_NAME, APP_1_PACKAGE_TITLE)
app2PackageItemInfo = packageItemInfoWithTitle(APP_2_PACKAGE_NAME, APP_2_PACKAGE_TITLE)
@@ -123,7 +123,7 @@
val widgetPickerData =
WidgetPickerData(
allWidgets = appTwoWidgetsListBaseEntries(),
- defaultWidgets = appTwoWidgetsListBaseEntries()
+ defaultWidgets = appTwoWidgetsListBaseEntries(),
)
val newWidgetData =
@@ -143,19 +143,19 @@
addAll(appOneWidgetsListBaseEntries())
addAll(appTwoWidgetsListBaseEntries())
},
- defaultWidgets = buildList { appTwoWidgetsListBaseEntries() }
+ defaultWidgets = buildList { appTwoWidgetsListBaseEntries() },
)
val recommendations: List<ItemInfo> =
listOf(
PendingAddWidgetInfo(
app1WidgetItem1.widgetInfo,
CONTAINER_WIDGETS_PREDICTION,
- CATEGORY_1
+ CATEGORY_1,
),
PendingAddWidgetInfo(
app2WidgetItem1.widgetInfo,
CONTAINER_WIDGETS_PREDICTION,
- CATEGORY_2
+ CATEGORY_2,
),
)
@@ -175,7 +175,7 @@
addAll(appOneWidgetsListBaseEntries())
addAll(appTwoWidgetsListBaseEntries())
},
- defaultWidgets = buildList { appTwoWidgetsListBaseEntries() }
+ defaultWidgets = buildList { appTwoWidgetsListBaseEntries() },
)
val recommendations: List<ItemInfo> =
listOf(
@@ -201,7 +201,7 @@
addAll(appTwoWidgetsListBaseEntries())
},
defaultWidgets = buildList { appTwoWidgetsListBaseEntries() },
- recommendations = mapOf(CATEGORY_1 to listOf(app1WidgetItem1))
+ recommendations = mapOf(CATEGORY_1 to listOf(app1WidgetItem1)),
)
val updatedData = widgetPickerData.withRecommendedWidgets(listOf())
@@ -242,7 +242,7 @@
addAll(appOneWidgetsListBaseEntries())
addAll(appTwoWidgetsListBaseEntries())
},
- defaultWidgets = buildList { addAll(appTwoWidgetsListBaseEntries()) }
+ defaultWidgets = buildList { addAll(appTwoWidgetsListBaseEntries()) },
)
val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
@@ -263,7 +263,7 @@
addAll(appTwoWidgetsListBaseEntries())
},
defaultWidgets =
- buildList { addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false)) }
+ buildList { addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false)) },
)
val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
@@ -271,7 +271,7 @@
findContentEntryForPackageUser(
widgetPickerData = widgetPickerData,
packageUserKey = app1PackageUserKey,
- fromDefaultWidgets = true
+ fromDefaultWidgets = true,
)
assertThat(contentEntry).isNotNull()
@@ -302,7 +302,7 @@
addAll(appTwoWidgetsListBaseEntries())
},
defaultWidgets =
- buildList { addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false)) }
+ buildList { addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false)) },
)
val widgets = findAllWidgetsForPackageUser(widgetPickerData, app1PackageUserKey)
@@ -314,9 +314,7 @@
@Test
fun findAllWidgetsForPackageUser_noMatch_returnsEmptyList() {
val widgetPickerData =
- WidgetPickerData(
- allWidgets = buildList { addAll(appTwoWidgetsListBaseEntries()) },
- )
+ WidgetPickerData(allWidgets = buildList { addAll(appTwoWidgetsListBaseEntries()) })
val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
val widgets = findAllWidgetsForPackageUser(widgetPickerData, app1PackageUserKey)
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 24d66a3..59f352b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -41,8 +41,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.search.SearchCallback;
@@ -87,7 +87,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doAnswer(invocation -> {
- ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ CachedObject componentWithLabel = invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
mTestProfile = new InvariantDeviceProfile();
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 7adb2b1..2f5fcfe 100644
--- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -28,7 +28,6 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
@@ -39,8 +38,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.BaseIconCache;
+import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ActivityContextWrapper;
@@ -99,7 +99,7 @@
initTestWidgets();
initTestShortcuts();
- doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+ doAnswer(invocation -> ((CachedObject) invocation.getArgument(0))
.getComponent().getPackageName())
.when(mIconCache).getTitleNoCache(any());
}
@@ -280,16 +280,15 @@
}
private void initTestShortcuts() {
- PackageManager packageManager = mContext.getPackageManager();
mShortcut1 = new WidgetItem(new TestShortcutConfigActivityInfo(
ComponentName.createRelative(TEST_PACKAGE, ".shortcut1"), UserHandle.CURRENT),
- mIconCache, packageManager);
+ mIconCache);
mShortcut2 = new WidgetItem(new TestShortcutConfigActivityInfo(
ComponentName.createRelative(TEST_PACKAGE, ".shortcut2"), UserHandle.CURRENT),
- mIconCache, packageManager);
+ mIconCache);
mShortcut3 = new WidgetItem(new TestShortcutConfigActivityInfo(
ComponentName.createRelative(TEST_PACKAGE, ".shortcut3"), UserHandle.CURRENT),
- mIconCache, packageManager);
+ mIconCache);
}
@@ -300,12 +299,12 @@
}
@Override
- public Drawable getFullResIcon(IconCache cache) {
+ public Drawable getFullResIcon(BaseIconCache cache) {
return null;
}
@Override
- public CharSequence getLabel(PackageManager pm) {
+ public CharSequence getLabel() {
return null;
}
}
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
index a991981..ca2ef42 100644
--- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
+++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
@@ -28,7 +28,6 @@
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.PROMISE_ICON_IDS
-import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.LauncherModelHelper
@@ -54,7 +53,6 @@
private val expectedAppPackage = "expectedAppPackage"
private val expectedInstallerPackage = "expectedInstallerPackage"
private val mockPackageInstaller: PackageInstaller = mock()
- private val mTracker: DaggerSingletonTracker = mock()
private lateinit var installSessionHelper: InstallSessionHelper
private lateinit var launcherApps: LauncherApps
@@ -64,7 +62,7 @@
whenever(packageManager.packageInstaller).thenReturn(mockPackageInstaller)
whenever(sandboxContext.packageName).thenReturn(expectedInstallerPackage)
launcherApps = sandboxContext.spyService(LauncherApps::class.java)
- installSessionHelper = InstallSessionHelper(sandboxContext, mTracker)
+ installSessionHelper = InstallSessionHelper(sandboxContext)
}
@Test
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index cee88ac..4ca6da8 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -20,7 +20,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
-import static com.android.launcher3.testing.shared.TestProtocol.WIDGET_CONFIG_NULL_EXTRA_INTENT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static org.junit.Assert.assertEquals;
@@ -415,6 +414,7 @@
public void verifyLauncherState() {
try {
// Limits UI tests affecting tests running after them.
+ mDevice.pressHome();
mLauncher.waitForLauncherInitialized();
if (mLauncherPid != 0) {
assertEquals("Launcher crashed, pid mismatch:",
@@ -563,23 +563,13 @@
@Override
public void onReceive(Context context, Intent intent) {
- Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, intent == null
- ? "AbstractLauncherUiTest.onReceive(): inputted intent NULL"
- : "AbstractLauncherUiTest.onReceive(): inputted intent NOT NULL");
mIntent = intent;
latch.countDown();
- Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT,
- "AbstractLauncherUiTest.onReceive() Countdown Latch started");
}
public Intent blockingGetIntent() throws InterruptedException {
- Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT,
- "AbstractLauncherUiTest.blockingGetIntent()");
assertTrue("Timed Out", latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS));
mTargetContext.unregisterReceiver(this);
- Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, mIntent == null
- ? "AbstractLauncherUiTest.onReceive(): mIntent NULL"
- : "AbstractLauncherUiTest.onReceive(): mIntent NOT NULL");
return mIntent;
}
diff --git a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
index b38dd4b..a45e3bb 100644
--- a/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/TaplWorkProfileTest.java
@@ -70,7 +70,6 @@
@Override
public void setUp() throws Exception {
super.setUp();
- initialize(this);
String output =
mDevice.executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
index e6e02b4..961e7fc 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
@@ -93,7 +93,7 @@
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
mLauncher.getWorkspace()
.openAllWidgets()
- .getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
+ .getWidget(mWidgetInfo.getLabel())
.dragToWorkspace(true, false);
// Widget id for which the config activity was opened
mWidgetId = monitor.getWidgetId();
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9c916fa..9a2147a 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -61,7 +61,7 @@
WidgetResizeFrame resizeFrame = mLauncher
.getWorkspace()
.openAllWidgets()
- .getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager()))
+ .getWidget(widgetInfo.getLabel())
.dragWidgetToWorkspace();
assertNotNull("Widget resize frame not shown after widget add", resizeFrame);
@@ -111,7 +111,7 @@
WidgetResizeFrame resizeFrame = mLauncher
.getWorkspace()
.openAllWidgets()
- .getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager()))
+ .getWidget(widgetInfo.getLabel())
.dragWidgetToWorkspace();
assertNotNull("Widget resize frame not shown after widget add", resizeFrame);
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
index 490cff2..237f2a9 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java
@@ -32,7 +32,6 @@
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
/**
@@ -50,12 +49,6 @@
return launcher.getWorkspace().getCurrentPage();
}
- @Before
- public void setUp() throws Exception {
- super.setUp();
- initialize(this);
- }
-
@After
public void tearDown() throws Exception {
if (mLauncherLayout != null) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 6387b05..3097d9c 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -116,8 +116,8 @@
}
/** Get widget with supplied text. */
- public Widget getWidget(String labelText) {
- return getWidget(labelText, null);
+ public Widget getWidget(CharSequence labelText) {
+ return getWidget(labelText.toString(), null);
}
/** Get widget with supplied text and app package */