Merge "Setup Dagger for Launcher (1/n)" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 0df6c36..40c3797 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -316,6 +316,7 @@
description: "Menu in Taskbar with options to launch and manage multiple instances of the same app"
bug: "355237285"
}
+
flag {
name: "navigate_to_child_preference"
namespace: "launcher"
@@ -332,3 +333,30 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "letter_fast_scroller"
+ namespace: "launcher"
+ description: "Change fast scroller to a lettered list"
+ bug: "358673724"
+}
+
+flag {
+ name: "enable_desktop_task_alpha_animation"
+ namespace: "launcher"
+ description: "Enables the animation of the desktop task's background view"
+ bug: "320307666"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "ignore_three_finger_trackpad_for_nav_handle_long_press"
+ namespace: "launcher"
+ description: "Ignore three finger trackpad event for nav handle long press"
+ bug: "342143522"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 11740ee..e11b00c 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -31,3 +31,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_large_desktop_windowing_tile"
+ namespace: "launcher_overview"
+ description: "Makes the desktop tiles larger and moves them to the front of the list in Overview."
+ bug: "353947137"
+}
+
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index 784a094..d90d916 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -15,22 +15,23 @@
-->
<com.android.quickstep.task.thumbnail.TaskThumbnailView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent" >
- <ImageView
+ <com.android.quickstep.views.FixedSizeImageView
android:id="@+id/task_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="matrix"
- android:visibility="gone"/>
+ android:visibility="invisible"/>
<com.android.quickstep.task.thumbnail.LiveTileView
android:id="@+id/task_thumbnail_live_tile"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone"/>
+ android:visibility="invisible"/>
<View
android:id="@+id/task_thumbnail_scrim"
@@ -39,16 +40,23 @@
android:background="@color/overview_foreground_scrim_color"
android:alpha="0" />
- <FrameLayout
- android:id="@+id/splash_container"
+ <View
+ android:id="@+id/splash_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone">
- <ImageView
- android:id="@+id/splash_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:importantForAccessibility="no" />
- </FrameLayout>
+ android:background="@android:color/black"
+ android:alpha="0"
+ android:importantForAccessibility="no" />
+
+ <com.android.quickstep.views.FixedSizeImageView
+ android:id="@+id/splash_icon"
+ android:layout_width="@dimen/task_thumbnail_splash_icon_size"
+ android:layout_height="@dimen/task_thumbnail_splash_icon_size"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:scaleType="fitCenter"
+ android:alpha="0"
+ android:importantForAccessibility="no" />
</com.android.quickstep.task.thumbnail.TaskThumbnailView>
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index c109328..c146b51 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Stoor app-paar"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tik op ’n ander app om verdeelde skerm te gebruik"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Kies ’n ander app as jy verdeelde skerm wil gebruik"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Kanselleer"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Verlaat verdeeldeskermkeuse"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies nog ’n app as jy verdeelde skerm wil gebruik"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index f144118..4ed1836 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"የመተግበሪያ ጥምረትን አስቀምጥ"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"የተከፈለ ማያ ገጽን ለመጠቀም ሌላ መተግበሪያ መታ ያድርጉ"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"የተከፈለ ማያ ገጽን ለመጠቀም ሌላ መተግበሪያ ይምረጡ"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"ይቅር"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ከተከፈለ ማያ ገፅ ምርጫ ይውጡ"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"የተከፈለ ማያ ገጽን ለመቀበል ሌላ መተግበሪያ ይምረጡ"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ይህ ድርጊት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 629580f..74509ac 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 252697c..ea8268e 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"আটাইবোৰ মচক"</string>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 7d7ed56..13b2c11 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Tətbiq cütünü saxla"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Bölünmüş ekran üçün başqa tətbiqə toxunun"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Bölünmüş ekrandan istifadə üçün başqa tətbiq seçin"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Ləğv edin"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Bölünmüş ekran seçimindən çıxın"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Bölünmüş ekrandan istifadə üçün başqa tətbiq seçin"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Bu əməliyyata tətbiq və ya təşkilatınız tərəfindən icazə verilmir"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index d8f608d..2a19691 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Sačuvaj par aplikacija"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu aplikaciju za podeljeni ekran"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Odaberite drugu aplikaciju da biste koristili podeljeni ekran"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Otkaži"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Izlazak iz biranja podeljenog ekrana"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu aplikaciju za podeljeni ekran"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili organizacija ne dozvoljavaju ovu radnju"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index b403e7f..7b99a6f 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 6fb6127..2ed92c3 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Изчистване на всички"</string>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 043c0d6..0762805 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"অ্যাপ পেয়ার সেভ করুন"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"স্প্লিট স্ক্রিন ব্যবহারের জন্য অ্যাপে ট্যাপ করুন"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"স্প্লিট স্ক্রিন ব্যবহার করতে অন্য অ্যাপ বেছে নিন"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"বাতিল করুন"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"স্প্লিট স্ক্রিন বেছে নেওয়ার বিকল্প থেকে বেরিয়ে আসুন"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"স্প্লিট স্ক্রিন ব্যবহার করতে অন্য অ্যাপ বেছে নিন"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"এই অ্যাপ বা আপনার প্রতিষ্ঠান এই অ্যাকশনটি পারফর্ম করার অনুমতি দেয়নি"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index af7fe61..283cb67 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rotirajte uređaj"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rotirajte uređaj da završite vodič za navigaciju pokretima"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Prevucite s krajnjeg desnog ili krajnjeg lijevog ruba"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Prevucite s krajnjeg desnog ili lijevog ruba"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Prevucite s desnog ili lijevog ruba prema sredini ekrana i pustite"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste kako prevući zdesna da se vratite. Sljedeće naučite kako prebacivati između aplikacija."</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Savladali ste pokret za vraćanje. Sljedeće naučite kako prebacivati između aplikacija."</string>
@@ -73,7 +74,7 @@
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Vodite računa da prevučete s donjeg ruba ekrana prema gore"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Pokušajte zadržati prozor duže prije puštanja"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Prevucite ravno nagore, a zatim zastanite"</string>
- <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste kako koristiti pokrete. Idite u Postavke da isključite pokrete."</string>
+ <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste kako koristiti pokrete. Da ih isključite, idite u Postavke."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Savladali ste pokret za prebacivanje između aplikacija"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prevucite da prebacujete između aplikacija"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Da se prebacujete između aplikacija, prevucite s dna ekrana nagore, zadržite, a zatim pustite."</string>
@@ -99,7 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Sačuvaj par aplikacija"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Dodirnite drugu apl. da koristite podijeljeni ekran"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Odaberite drugu aplikaciju da koristite podijeljeni ekran"</string>
- <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Odustani"</string>
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Otkaži"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Izlaz iz odabira podijeljenog ekrana"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Odaberite drugu apl. da koristite podijeljeni ekran"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Ovu radnju ne dozvoljava aplikacija ili vaša organizacija"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index cc3112a..460f2fc 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Esborra-ho tot"</string>
@@ -59,7 +60,7 @@
<string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Per tornar a la pantalla anterior, llisca amb dos dits des de l\'extrem esquerre o dret cap al centre de la pantalla."</string>
<string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Torna enrere"</string>
<string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Llisca des de la vora esquerra o dreta cap al centre de la pantalla."</string>
- <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Assegura\'t de lliscar cap amunt des de la part inferior de la pantalla"</string>
+ <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Assegura\'t de lliscar cap amunt des de la part inferior de la pantalla."</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Assegura\'t de no aturar-te abans de deixar anar."</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Assegura\'t de lliscar recte cap amunt."</string>
<string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Has completat el gest per anar a la pantalla d\'inici. Ara, descobreix com pots tornar enrere."</string>
@@ -72,7 +73,7 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Ben fet!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Assegura\'t de lliscar cap amunt des de la part inferior de la pantalla."</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Prova de mantenir premuda la finestra durant més temps abans de deixar-la anar"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Assegura\'t de lliscar directament cap amunt i després aturar-te"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Assegura\'t de lliscar directament cap amunt i després aturar-te."</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Has après a utilitzar els gestos. Per desactivar-los, ves a Configuració."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Has completat el gest per canviar d\'aplicació"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Llisca per canviar d\'aplicació"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Desa la parella d\'apps"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Toca una altra app per utilitzar pantalla dividida"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Tria una altra aplicació per utilitzar la pantalla dividida"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancel·la"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Surt de la selecció de pantalla dividida"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Tria una altra app per utilitzar pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 546aa1e..1e5df41 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
@@ -91,7 +92,7 @@
<string name="allset_hint" msgid="459504134589971527">"Přejetím nahoru se vrátíte na plochu"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Klepnutím na tlačítko plochy se vrátíte na plochu"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> můžete začít používat"</string>
- <string name="default_device_name" msgid="6660656727127422487">"zařízení"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"Zařízení"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Nastavení navigace v systému"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Sdílet"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Snímek obrazovky"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Uložit dvojici aplikací"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Obrazovku rozdělíte klepnutím na jinou aplikaci"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Výběrem další aplikace rozdělíte obrazovku"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Zrušit"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Výběr opuštění rozdělené obrazovky"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Vyberte podporovanou aplikaci"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikace nebo organizace zakazuje tuto akci"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index ae10343..4f61b86 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Gem appsammenknytning"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tryk på en anden app for at bruge opdelt skærm"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Vælg en anden app for at bruge opdelt skærm"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Annuller"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Luk valg af opdelt skærm"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Vælg en anden app for at bruge opdelt skærm"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 03b5bb9..ac5e7aa 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"App-Paar speichern"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Für Splitscreen auf weitere App tippen"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Für Splitscreen andere App auswählen"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Abbrechen"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Splitscreen-Auswahl beenden"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Für Splitscreen andere App auswählen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Die App oder deine Organisation lässt diese Aktion nicht zu"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index a717678..ddd81d2 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
@@ -90,7 +91,7 @@
<string name="allset_title" msgid="5021126669778966707">"Όλα έτοιμα!"</string>
<string name="allset_hint" msgid="459504134589971527">"Σύρετε προς τα επάνω για να μεταβείτε στην αρχική σελίδα"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Πατήστε το κουμπί αρχικής οθόνης για να μεταβείτε στην αρχική οθόνη"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε το/τη <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Είστε έτοιμοι να ξεκινήσετε να χρησιμοποιείτε τη <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"συσκευή"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ρυθμίσεις πλοήγησης συστήματος"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Κοινοποίηση"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Αποθήκ. ζεύγ. εφαρμ."</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Πατήστε άλλη εφαρμογή για διαχωρισμό οθόνης"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Επιλέξτε άλλη εφαρμογή για διαχωρισμό οθόνης"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Ακύρωση"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Έξοδος από την επιλογή διαχωρισμού οθόνης"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Επιλέξτε άλλη εφαρμογή για διαχωρισμό οθόνης"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Αυτή η ενέργεια δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας."</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index d28cbf0..bc2f91a 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/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_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>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Save app pair"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Choose another app to use split screen"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancel"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index e069b8f..f4396fa 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_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>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index d28cbf0..bc2f91a 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/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_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>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Save app pair"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Choose another app to use split screen"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancel"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index d28cbf0..bc2f91a 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/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_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>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Save app pair"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tap another app to use split screen"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Choose another app to use split screen"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancel"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Exit split screen selection"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Choose another app to use split screen"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index 777db28..65f0d39 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/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_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>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 3d78c71..87a05ef 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Cerrar todo"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Guardar vinculación"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Presiona otra app para usar la pantalla dividida"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Elige otra app para usar la pantalla dividida"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancelar"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Salir de la selección de pantalla dividida"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Elige otra app para usar la pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"La app o tu organización no permiten realizar esta acción"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index f878954..8bd5fb8 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 4468533..32d29c8 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Salvesta rakendusepaar"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Jagatud ekraanikuva kasutamiseks puudutage muud rakendust"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Valige jagatud ekraanikuva jaoks muu rakendus."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Tühista"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Jagatud ekraanikuva valikust väljumine"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Valige jagatud ekraanikuva jaoks muu rakendus."</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Rakendus või teie organisatsioon on selle toimingu keelanud"</string>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index fe138b8..210e2a2 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Gorde aplikazio parea"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Sakatu beste aplikazio bat pantaila zatitzeko"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Pantaila zatitua erabiltzeko, aukeratu beste aplikazio bat"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Utzi"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Irten pantaila zatituaren hautapenetik"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pantaila zatitzeko, aukeratu beste aplikazio bat"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikazioak edo erakundeak ez du eman ekintza hori gauzatzeko baimena"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index cb88eba..ef21402 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
@@ -103,7 +104,7 @@
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"خروج از انتخاب صفحهٔ دونیمه"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"انتخاب برنامهای دیگر برای استفاده از صفحه دونیمه"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"برنامه یا سازمان شما اجازه نمیدهد این کنش انجام شود."</string>
- <string name="split_widgets_not_supported" msgid="1355743038053053866">"درحالحاضر از ابزارکها پشتیبانی نمیشود، لطفاً برنامه دیگری را انتخاب کنید"</string>
+ <string name="split_widgets_not_supported" msgid="1355743038053053866">"درحالحاضر از ابزارهها پشتیبانی نمیشود، لطفاً برنامه دیگری را انتخاب کنید"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"آموزش گامبهگام پیمایش رد شود؟"</string>
<string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"میتوانید آن را بعداً در برنامه <xliff:g id="NAME">%1$s</xliff:g> پیدا کنید"</string>
<string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"لغو"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 4d610f2..8288cb5 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Poista kaikki"</string>
@@ -72,7 +73,7 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Hienoa!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pyyhkäise ylös näytön alareunasta"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Kokeile pitää ikkunaa painettuna pidempään ennen kuin päästät irti"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Muista pyyhkäistä suoraan ylöspäin ja pysähdy sitten"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pyyhkäise suoraan ylöspäin ja pysähdy sitten"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Olet oppinut käyttämään eleitä. Jos haluat laittaa eleet pois päältä, avaa Asetukset."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Olet oppinut sovellusten vaihtamiseleen"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vaihda sovellusta pyyhkäisemällä"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Tallenna pari"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Peru"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Poistu jaetun näytön valinnasta"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Sovellus tai organisaatio ei salli tätä toimintoa"</string>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 179be5f..e3089b6 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 8568c40..b68378b 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
@@ -96,11 +97,10 @@
<string name="action_share" msgid="2648470652637092375">"Partager"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
<string name="action_split" msgid="2098009717623550676">"Partager"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"Enregistrer la paire d\'applis"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"Enregistrer une paire d\'applis"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Appuyez sur autre appli pour l\'écran partagé"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Sélectionnez une autre appli pour utiliser l\'écran partagé."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Annuler"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Quitter la sélection de l\'écran partagé"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Sélect. autre appli pour utiliser l\'écran partagé"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Cette action n\'est pas autorisée par l\'application ou par votre organisation"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index e6400ca..b4c91ba 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Gardar parella apps"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Para usar a pantalla dividida, toca outra app"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Escolle outra aplicación para usar a pantalla dividida."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancelar"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Saír da selección de pantalla dividida"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolle outra app para usar a pantalla dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"A aplicación ou a túa organización non permite realizar esta acción"</string>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 2adf594..dcbcd58 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ઍપની જોડી સાચવો"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"વિભાજિત સ્ક્રીન વાપરવા, કોઈ અન્ય ઍપ પર ટૅપ કરો"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"વિભાજિત સ્ક્રીનની સુવિધાનો ઉપયોગ કરવા કોઈ અન્ય ઍપ પસંદ કરો"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"રદ કરો"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"\'સ્ક્રીનને વિભાજિત કરો\' પસંદગીમાંથી બહાર નીકળો"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"સ્ક્રીન વિભાજનનો ઉપયોગ કરવા કોઈ અન્ય ઍપ પસંદ કરો"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ઍપ કે તમારી સંસ્થા દ્વારા આ ક્રિયા કરવાની મંજૂરી નથી"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 56d2b39..137f809c 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"सभी हटाएं"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 97f4ee0..6178570 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Zakrenite uređaj"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Zakrenite uređaj da biste dovršili vodič o navigaciji pokretima"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Pazite da prijeđete prstom od krajnjeg desnog ili krajnjeg lijevog ruba"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Prijeđite prstom od krajnjeg desnog ili krajnjeg lijevog ruba"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Pazite da prijeđete prstom od desnog ili lijevog ruba do sredine zaslona i podignite prst"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste kako prijeći prstom zdesna da biste se vratili. Sad saznajte kako promijeniti aplikaciju."</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Izvršili ste pokret za povratak. Sad saznajte kako promijeniti aplikaciju."</string>
@@ -70,9 +71,9 @@
<string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Otvaranje početnog zaslona"</string>
<string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Prijeđite prstom od dna zaslona prema gore"</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Sjajno!"</string>
- <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona"</string>
+ <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Prijeđite prstom prema gore od donjeg ruba zaslona"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Pokušajte zadržati prozor dulje prije podizanja prsta"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pazite da prijeđete prstom ravno prema gore, a zatim zastanete"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Prijeđite prstom ravno prema gore, a zatim zastanite"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste koristiti pokrete. Pokrete možete isključiti u postavkama."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Izvršili ste pokret za promjenu aplikacije"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Povlačenje prstom za promjenu aplikacije"</string>
@@ -91,7 +92,7 @@
<string name="allset_hint" msgid="459504134589971527">"Prijeđite prstom prema gore da biste otvorili početni zaslon"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Dodirnite gumb početnog zaslona da biste prešli na početni zaslon"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> je spreman za početak upotrebe"</string>
- <string name="default_device_name" msgid="6660656727127422487">"uređaj"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"Uređaj"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Postavke navigacije sustavom"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 2d0bd18..99c39f1 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Összes törlése"</string>
@@ -59,7 +60,7 @@
<string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Ha vissza szeretne térni a legutóbbi képernyőre, csúsztasson gyorsan két ujjal a képernyő bal vagy jobb széléről a közepe felé."</string>
<string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Vissza"</string>
<string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Csúsztasson bal vagy jobb szélről a képernyő közepe felé."</string>
- <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Csúsztasson felfelé a képernyő aljától."</string>
+ <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Csúsztasson felfelé a képernyő alsó szélétől."</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Ne álljon meg, mielőtt elengedi a képernyőt."</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Csúsztasson egyenesen felfelé."</string>
<string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Teljesítette a kezdőképernyőre lépés kézmozdulatát. Most megtanulhatja, hogyan léphet vissza."</string>
@@ -68,9 +69,9 @@
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Csúsztassa ujját felfelé a képernyő aljától. Ez a mozdulat mindig a kezdőképernyőre visz."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Csúsztasson felfelé két ujjal a képernyő aljáról. Ez a kézmozdulat mindig a kezdőképernyőre viszi."</string>
<string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Ugrás a kezdőképernyőre"</string>
- <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Húzza ujját felfelé a képernyő aljától."</string>
+ <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Csúsztasson felfelé a képernyő aljától."</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Kiváló!"</string>
- <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Csúsztasson felfelé a képernyő aljától."</string>
+ <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Csúsztasson felfelé a képernyő alsó szélétől."</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Próbálja tovább lenyomva tartani az ablakot, mielőtt elengedi a képernyőt."</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Csúsztasson egyenesen felfelé, majd várjon egy kicsit"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Eddig megismerhette a kézmozdulatok használatát. A kézmozdulatokat a Beállításokban kapcsolhatja ki."</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"App-pár mentése"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Koppintson másik appra az osztott képernyőhöz"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Válasszon másik appot a képernyő felosztásához"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Mégse"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Kilépés az osztott képernyő elemeinek kiválasztásából"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Válasszon másik appot a képernyő felosztásához"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Az alkalmazás vagy az Ön szervezete nem engedélyezi ezt a műveletet"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 947e34e..0dda363 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 16334ca..b6e492d 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Simpan pasangan apl"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Ketuk aplikasi lain untuk memakai layar terpisah"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Pilih aplikasi lain untuk dibuka di layar terpisah"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Batal"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Keluar dari pemilihan layar terpisah"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk dibuka di layar terpisah"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index ed1cf85..ad388c0 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Vista forritapar"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Ýttu á annað forrit til að nota skjáskiptingu"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Veldu annað forrit til að nota skjáskiptingu"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Hætta við"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Loka skjáskiptingu"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Veldu annað forrit til að nota skjáskiptingu"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Forritið eða fyrirtækið leyfir ekki þessa aðgerð"</string>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 29a84dd..9ddd4da 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Salva coppia di app"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tocca un\'altra app per usare lo schermo diviso"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Scegli un\'altra app per usare lo schermo diviso"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Annulla"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Esci dalla selezione dello schermo diviso"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Scegli un\'altra app per usare lo schermo diviso"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Questa azione non è consentita dall\'app o dall\'organizzazione"</string>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index e30f7df..c5c7145 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
@@ -72,8 +73,8 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"מעולה!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"חשוב להחליק למעלה מהקצה התחתון של המסך"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"כדאי לנסות להחזיק את החלון זמן רב יותר לפני שחרור האצבע"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"חשוב להחליק ישר למעלה ואז להמתין"</string>
- <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"למדת איך להשתמש בתנועות. ניתן להשבית את התנועות ב\'הגדרות\'."</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"חשוב להחליק למעלה בקו ישר ואז לעצור"</string>
+ <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"למדת איך להשתמש בתנועות. אפשר להשבית את התנועות ב\'הגדרות\'."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"השלמת את תנועת המעבר בין האפליקציות"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"יש להחליק כדי לעבור בין אפליקציות"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"כדי לעבור בין אפלקציות, יש להחליק למעלה מתחתית המסך, להחזיק ולאחר מכן לשחרר."</string>
@@ -89,7 +90,7 @@
<string name="gesture_tutorial_step" msgid="1279786122817620968">"מדריך <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"הכול מוכן!"</string>
<string name="allset_hint" msgid="459504134589971527">"כדי לחזור לדף הבית, צריך להחליק למעלה"</string>
- <string name="allset_button_hint" msgid="2395219947744706291">"כדי לעבור אל מסך הבית יש להקיש על הלחצן הראשי"</string>
+ <string name="allset_button_hint" msgid="2395219947744706291">"כדי לעבור אל מסך הבית צריך להקיש על הלחצן הראשי"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"הכול מוכן ואפשר להתחיל להשתמש ב<xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"מכשיר"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"הגדרות הניווט של המערכת"</annotation></string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"שמירת צמד אפליקציות"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"צריך להקיש על אפליקציה אחרת כדי להשתמש במסך מפוצל"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"כדי להשתמש במסך מפוצל צריך לבחור אפליקציה אחרת"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"ביטול"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"יציאה מתצוגת מסך מפוצל"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"כדי להשתמש במסך מפוצל צריך לבחור אפליקציה אחרת"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"האפליקציה או הארגון שלך אינם מתירים את הפעולה הזאת"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index bb6d07b..b2732a6 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 0ffc3e8..a23201d 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 8bf7111..a7b3d6f 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Қолданбаларды жұптауды сақтау"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Экранды бөлу режимін пайдалану үшін басқа қолданбаны түртіңіз."</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Экранды бөлу үшін басқа қолданбаны таңдаңыз."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Бас тарту"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Экранды бөлу режимінен шығу"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Экранды бөлу үшін басқа қолданбаны таңдаңыз."</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Бұл әрекетке қолданба не ұйым рұқсат етпейді."</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 06cd015..f83b09b 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"សម្អាតទាំងអស់"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"រក្សាទុកគូកម្មវិធី"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"ចុចកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"ជ្រើសរើសកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"បោះបង់"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ចាកចេញពីការជ្រើសរើសរបស់មុខងារបំបែកអេក្រង់"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"ជ្រើសរើសកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"សកម្មភាពនេះមិនត្រូវបានអនុញ្ញាតដោយកម្មវិធី ឬស្ថាប័នរបស់អ្នកទេ"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 26f1d6f..48093b3 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
@@ -73,13 +74,13 @@
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ಬೆರಳನ್ನು ಮೇಲೆತ್ತುವ ಮೊದಲು ವಿಂಡೋವನ್ನು ಹೆಚ್ಚು ಸಮಯ ಹಿಡಿದಿಡಲು ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ, ನಂತರ ವಿರಾಮಗೊಳಿಸಿ"</string>
- <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ಗೆಶ್ಚರ್ಗಳನ್ನು ಬಳಸುವುದು ಹೇಗೆಂದು ನೀವು ತಿಳಿದುಕೊಂಡಿರುವಿರಿ. ಗೆಶ್ಚರ್ಗಳನ್ನು ಆಫ್ ಮಾಡಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ."</string>
+ <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ಜೆಶ್ಚರ್ಗಳನ್ನು ಬಳಸುವುದು ಹೇಗೆಂದು ನೀವು ತಿಳಿದುಕೊಂಡಿರುವಿರಿ. ಜೆಶ್ಚರ್ಗಳನ್ನು ಆಫ್ ಮಾಡಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ನೀವು ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಾಯಿಸುವ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ಆ್ಯಪ್ಗಳ ನಡುವೆ ಬದಲಿಸಲು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಿ, ನಂತರ ಬಿಟ್ಟುಬಿಡಿ."</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ಆ್ಯಪ್ಗಳ ನಡುವೆ ಬದಲಿಸಲು, 2 ಬೆರಳುಗಳಿಂದ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಿ, ನಂತರ ಬಿಟ್ಟುಬಿಡಿ."</string>
<string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ಆ್ಯಪ್ಗಳನ್ನು ಬದಲಿಸಿ"</string>
- <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹೋಲ್ಡ್ ಮಾಡಿ, ನಂತರ ಬಿಡುಗಡೆ ಮಾಡಿ"</string>
+ <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಹೋಲ್ಡ್ ಮಾಡಿ, ನಂತರ ಬಿಟ್ಟುಬಿಡಿ"</string>
<string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ಭೇಷ್!"</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ಸಂಪೂರ್ಣ ಸಿದ್ಧವಾಗಿದೆ"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ಮುಗಿದಿದೆ"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 04fd457..1aca7a2 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"모두 삭제"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"앱 페어링 저장"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"다른 앱을 탭하여 화면 분할 사용"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"화면 분할을 사용하려면 다른 앱을 선택하세요."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"취소"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"화면 분할 선택 종료"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"화면 분할을 사용하려면 다른 앱을 선택하세요."</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"이 작업은 앱 또는 조직에서 허용되지 않습니다."</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index f0156de..e440b40 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Колдонмолорду сактап коюу"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Экранды бөлүү үчүн башка колдонмону таптап коюңуз"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Экранды бөлүү үчүн башка колдонмону тандаңыз"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Жокко чыгаруу"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Тандалган экранды бөлүүдөн чыгуу"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Экранды бөлүү үчүн башка колдонмону тандаңыз"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Бул аракетти аткарууга колдонмо же ишканаңыз тыюу салган"</string>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 81a52b1..a2ba90c 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ບັນທຶກຈັບຄູ່ແອັບ"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"ແຕະແອັບອື່ນເພື່ອໃຊ້ໜ້າຈໍແຍກ"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"ເລືອກແອັບອື່ນເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"ຍົກເລີກ"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ອອກຈາກາກນເລືອກການແບ່ງໜ້າຈໍ"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"ເລືອກແອັບອື່ນເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ແອັບ ຫຼື ອົງການຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ໃຊ້ຄຳສັ່ງນີ້"</string>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index ec45620..a70f1bd 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Išvalyti viską"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Išsaug. progr. porą"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Išskaidyto ekrano režimas palietus kitą programą"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Išskaidyto ekrano režimą naudokite kita programa"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Atšaukti"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Išeiti iš išskaidyto ekrano pasirinkimo"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Išskaidyto ekrano režimą naudokite kita programa"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Jūsų organizacijoje arba naudojant šią programą neleidžiama atlikti šio veiksmo"</string>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 4474e7c..b3d9c01 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Notīrīt visu"</string>
@@ -67,7 +68,7 @@
<string name="home_gesture_intro_title" msgid="836590312858441830">"Vilkšana, lai pārietu uz sākumu"</string>
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Velciet augšup no ekrāna apakšdaļas. Ar šo žestu vienmēr varat atvērt sākuma ekrānu."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Ar 2 pirkstiem velciet augšup no ekrāna apakšdaļas. Ar šo žestu vienmēr varat atvērt sākuma ekrānu."</string>
- <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Došanās uz sākuma ekrānu"</string>
+ <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Doties uz sākuma ekrānu"</string>
<string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Velciet augšup no ekrāna apakšdaļas."</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Lieliski!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Jāvelk augšup no ekrāna apakšmalas."</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Saglabāt pāri"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Lai sadalītu ekrānu, pieskarieties citai lietotnei"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Izvēlieties citu lietotni, lai sadalītu ekrānu"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Atcelt"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Izejiet no ekrāna sadalīšanas režīma atlases."</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Izvēlieties citu lietotni, lai sadalītu ekrānu"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Lietotne vai jūsu organizācija neatļauj veikt šo darbību."</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index f503e59..2cd67c2 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Избриши ги сите"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Зачувај го паров"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Допрете друга аплик. за да користите поделен екран"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Изберете друга апликација за да користите поделен екран"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Откажи"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Излези од изборот на поделен екран"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Изберете друга апликација за да користите поделен екран"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Апликацијата или вашата организација не го дозволува дејствово"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 5448f60..74271a6 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്ക്കുക"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ആപ്പ് ജോടി സംരക്ഷിക്കൂ"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"സ്പ്ലിറ്റ് സ്ക്രീനിന് മറ്റൊരു ആപ്പിൽ ടാപ്പ് ചെയ്യൂ"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കാൻ മറ്റൊരു ആപ്പ് തിരഞ്ഞെടുക്കൂ"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"റദ്ദാക്കുക"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"സ്ക്രീൻ വിഭജന തിരഞ്ഞെടുപ്പിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"സ്ക്രീൻ വിഭജന മോഡിന് മറ്റൊരു ആപ്പ് തിരഞ്ഞെടുക്കൂ"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ഈ നടപടി എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 18c66d7..fd71823 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг арилгах"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 45f768a..685b38d 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 5d56cd1..cb72b82 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
@@ -68,18 +69,18 @@
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Leret ke atas dari bahagian bawah skrin. Gerak isyarat ini sentiasa membawa anda ke Skrin utama."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Leret ke atas dengan 2 jari dari bawah skrin. Gerak isyarat ini sentiasa bawa anda ke Skrin utama."</string>
<string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Pergi ke skrin utama"</string>
- <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Leret ke atas dari bahagian bawah skrin anda"</string>
+ <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Leret ke atas dari bahagian bawah skrin"</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Syabas!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Pastikan anda meleret ke atas dari sisi bahagian bawah skrin"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Cuba tahan tetingkap untuk tempoh yang lebih lama sebelum melepaskan"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pastikan anda meleret terus ke atas, kemudian menjeda"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Pastikan anda meleret lurus ke atas, kemudian berhenti seketika"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Anda sudah belajar cara menggunakan gerak isyarat. Untuk mematikan gerak isyarat, pergi ke Tetapan."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Anda telah melengkapkan gerak isyarat menukar apl"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Leret untuk menukar apl"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Untuk beralih antara apl, leret ke atas dari bahagian bawah skrin anda, tahan, kemudian lepaskan."</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Untuk beralih antara apl, leret ke atas dengan 2 jari dari bawah skrin, tahan, kemudian lepaskan."</string>
<string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Beralih antara apl"</string>
- <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Leret ke atas dari bahagian bawah skrin anda, tahan, kemudian lepaskan"</string>
+ <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Leret ke atas dari bahagian bawah skrin, tahan, kemudian lepas"</string>
<string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Syabas!"</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Selesai"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Selesai"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 06740db..a3c462d 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးရှင်းရန်"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ကြိုတင်မှန်းဆထားသော အက်ပ်− <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"သင့်စက်ကို လှည့်ပါ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"လက်ဟန်ဖြင့် လမ်းညွှန်ခြင်း ရှင်းလင်းပို့ချချက်အား အပြီးသတ်ရန် သင့်စက်ကို လှည့်ပါ"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ညာ (သို့) ဘယ်အစွန်း၏ ခပ်လှမ်းလှမ်းမှ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ညာ (သို့) ဘယ်ဘက်အစွန်ဆုံးမှ ပွတ်ဆွဲကြောင်း သေချာပါစေ"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ဖန်သားပြင်၏ ညာ (သို့) ဘယ်အစွန်းမှ အလယ်သို့ ပွတ်ဆွဲပြီး လွှတ်လိုက်ကြောင်း သေချာပါစေ"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"နောက်ပြန်သွားရန် ညာဘက်မှပွတ်ဆွဲနည်းကို သိသွားပါပြီ။ နောက်အဆင့်တွင် အက်ပ်များပြောင်းနည်းကို လေ့လာပါ။"</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"နောက်ဆုတ်လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။ နောက်အဆင့်တွင် အက်ပ်များပြောင်းနည်းကို လေ့လာပါ။"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"အက်ပ်အတွဲ သိမ်းရန်"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"မျက်နှာပြင် ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"မလုပ်တော့"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း ရွေးချယ်မှုမှ ထွက်ရန်"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"မျက်နှာပြင်ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ဤလုပ်ဆောင်ချက်ကို အက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 6a31efa..e486ad8 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Fjern alt"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Lagre apptilkobling"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Trykk på en annen app for å bruke delt skjerm"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Velg en annen app for å bruke delt skjerm"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Avbryt"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Avslutt valg av delt skjerm"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Velg en annen app for å bruke delt skjerm"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisasjonen din tillater ikke denne handlingen"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index b4a0136..25c5901 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"सबै मेटाउनुहोस्"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"एपको पेयर सेभ गर्नुहोस्"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"स्प्लिटस्क्रिन प्रयोग गर्न अर्को एपमा ट्याप गर्नु…"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"स्प्लिट स्क्रिन प्रयोग गर्न अर्को एप रोज्नुहोस्"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"रद्द गर्नुहोस्"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"स्प्लिट स्क्रिन मोडबाट बाहिरिनुहोस्"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"स्प्लिट स्क्रिन प्रयोग गर्न अर्को एप रोज्नुहोस्"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"यो एप वा तपाईंको सङ्गठनले यो कारबाही गर्ने अनुमति दिँदैन"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 0cd8141..2b5bd49 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Alles wissen"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 97b0493..6628826 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ପୂର୍ବାନୁମାନ କରାଯାଇଥିବା ଆପ୍: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"ଜେଶ୍ଚର ନାଭିଗେସନ ଟ୍ୟୁଟୋରିଆଲ ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ଦୟାକରି ଆପଣଙ୍କ ଡିଭାଇସ ରୋଟେଟ କରନ୍ତୁ"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ଆପଣ ସ୍କ୍ରିନର ଏକଦମ୍-ଡାହାଣ ବା ବାମ ଧାରରୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ଆପଣ ସ୍କ୍ରିନର ଏକଦମ-ଡାହାଣ ବା ବାମ ଧାରରୁ ସ୍ୱାଇପ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ଆପଣ ସ୍କ୍ରିନର ଡାହାଣ ବା ବାମ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ କରି ଛାଡ଼ି ଦେଉଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"ଆପଣ ଡାହାଣରୁ ସ୍ୱାଇପ୍ କରି ପଛକୁ କିପରି ଫେରିବେ ତାହା ଜାଣିଲେ। ତା\'ପରେ, ଆପକୁ କିପରି ସ୍ୱିଚ୍ କରିବେ ତାହା ଜାଣନ୍ତୁ।"</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ଆପଗୁଡ଼ିକୁ କିପରି ସ୍ୱିଚ୍ କରିବେ ତାହା ଜାଣନ୍ତୁ।"</string>
@@ -72,14 +73,14 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"ବଢ଼ିଆ କାମ!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ୱିଣ୍ଡୋକୁ ରିଲିଜ୍ କରିବା ପୂର୍ବରୁ ଅଧିକ ସମୟ ଧରି ରଖିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରି ତା\'ପରେ ବିରତ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ କରି ତା\'ପରେ ବିରତ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"ଜେଶ୍ଚରଗୁଡ଼ିକୁ କିପରି ବ୍ୟବହାର କରାଯିବ ଆପଣ ତାହା ଶିଖିଛନ୍ତି। ଜେଶ୍ଚରଗୁଡ଼ିକୁ ବନ୍ଦ କରିବାକୁ, ସେଟିଂସକୁ ଯାଆନ୍ତୁ।"</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ଆପଣ \'ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"ଆପ୍ସ ମଧ୍ୟରେ ସୁଇଚ କରିବାକୁ, ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ, ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ କରନ୍ତୁ।"</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"ଆପ୍ସ ମଧ୍ୟରେ ସ୍ୱିଚ କରିବାକୁ, 2ଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ କର।"</string>
<string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"ଆପ୍ସ ସୁଇଚ କରନ୍ତୁ"</string>
- <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରି ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ୍ କରନ୍ତୁ"</string>
+ <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ କରନ୍ତୁ"</string>
<string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"ବହୁତ ବଢ଼ିଆ!"</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ସବୁ ପ୍ରସ୍ତୁତ"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"ହୋଇଗଲା"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ଆପ ପେୟାର ସେଭ କରନ୍ତୁ"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"ସ୍ପ୍ଲିଟସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପରେ ଟାପ କର"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପ ବାଛନ୍ତୁ"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ଚୟନରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଅନ୍ୟ ଏକ ଆପ ବାଛନ୍ତୁ"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ଆପ୍ କିମ୍ବା ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ଏହି କାର୍ଯ୍ୟକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index bd7e4d8..6d4d287 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ਐਪ ਜੋੜਾਬੱਧ ਰੱਖਿਅਤ ਕਰੋ"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਨੂੰ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਨੂੰ ਚੁਣੋ"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"ਰੱਦ ਕਰੋ"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਚੋਣ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਰਤਣ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਨੂੰ ਚੁਣੋ"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਕਾਰਵਾਈ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 942cdde..f001a88 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Wyczyść wszystko"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 50058c9..1d65cce 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Rode o dispositivo"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Rode o seu dispositivo para concluir o tutorial de navegação por gestos"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Deslize rapidamente a partir da extremidade mais à direita ou mais à esquerda"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Deslize a partir da extremidade mais à direita ou mais à esquerda"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Deslize rapidamente a partir da extremidade esquerda ou direita até ao centro do ecrã e solte"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Aprendeu a deslizar a partir da direita para retroceder. A seguir, saiba como alternar entre apps."</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Concluiu o gesto para retroceder. A seguir, saiba como alternar entre apps."</string>
@@ -58,8 +59,8 @@
<string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para voltar ao último ecrã, deslize rapidamente do limite esquerdo ou direito até ao centro do ecrã."</string>
<string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Para voltar ao último ecrã, deslize rapidamente com 2 dedos a partir da extremidade esquerda ou direita até ao centro do ecrã."</string>
<string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Retroceder"</string>
- <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Deslize rapidamente a partir da extremidade esquerda ou direita para o meio do ecrã"</string>
- <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Deslize rapidamente com o dedo a partir do limite inferior do ecrã"</string>
+ <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Deslize a partir da extremidade esquerda ou direita até ao centro do ecrã"</string>
+ <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Deslize a partir do limite inferior do ecrã"</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Não faça uma pausa antes de soltar"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Deslize rapidamente com o dedo para cima"</string>
<string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Concluiu o gesto para aceder ao ecrã principal. A seguir, saiba como retroceder."</string>
@@ -68,18 +69,18 @@
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Deslize rapidamente para cima a partir da parte inferior. Este gesto abre sempre o ecrã principal."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Deslize rapidamente para cima com 2 dedos no fundo do ecrã. Este gesto abre sempre o ecrã principal."</string>
<string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Aceda ao ecrã principal"</string>
- <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Deslize rapidamente para cima a partir da parte inferior do ecrã"</string>
+ <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Deslize para cima a partir da parte inferior do ecrã"</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Muito bem!"</string>
- <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Deslize rapidamente com o dedo a partir do limite inferior do ecrã"</string>
+ <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Deslize a partir do limite inferior do ecrã"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Experimente premir a janela durante mais tempo antes de soltar"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Garanta que desliza rapidamente com o dedo para cima e, em seguida, faz uma pausa"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Deslize para cima e pause"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Aprendeu a utilizar gestos. Para desativar os gestos, aceda às Definições."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Concluiu o gesto para alternar entre apps"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslize rapidamente com o dedo para alternar entre apps"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Para alternar entre apps, deslize para cima sem soltar a partir da parte inferior do ecrã e solte."</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Para mudar de app, deslize rapidamente para cima com 2 dedos sem soltar no fundo do ecrã e solte."</string>
<string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Mude de app"</string>
- <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Deslize rapidamente para cima a partir da parte inferior do ecrã sem soltar e, em seguida, solte"</string>
+ <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Deslize para cima a partir da parte inferior do ecrã , detenha o gesto e solte"</string>
<string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Muito bem!"</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Está tudo pronto"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Concluído"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 6ad7b2e..e094908 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Remover tudo"</string>
@@ -72,7 +73,7 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Muito bem!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Deslize da borda inferior da tela para cima"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Mantenha a janela pressionada por mais tempo antes de soltar"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Deslize para cima e pare"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Deslize para cima em linha reta e pare"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Você aprendeu. Para desativar os gestos, acesse as Configurações."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Você concluiu o gesto para mudar de app"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslizar para trocar de app"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Salvar par de apps"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Toque em outro app para usar a tela dividida"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Escolha outro app para usar na tela dividida"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancelar"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Sair da seleção de tela dividida"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Escolha outro app para usar na tela dividida"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 9b264b8..be813d5 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Șterge tot"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Salvează perechea de aplicații"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Atinge altă aplicație pentru ecranul împărțit"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Alege altă aplicație pentru a folosi ecranul împărțit"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Anulează"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ieși din selecția cu ecran împărțit"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Alege altă aplicație pentru ecranul împărțit"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Această acțiune nu este permisă de aplicație sau de organizația ta"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 5bcae00..5e1c79d 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистить все"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Сохранить приложения"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Для разделения экрана выберите другое приложение."</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Чтобы использовать разделенный экран, выберите другое приложение."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Отмена"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Выйдите из режима разделения экрана."</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Выберите другое приложение для разделения экрана."</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Это действие заблокировано приложением или организацией."</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index b23617f..1b6ec0f 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"යෙදුම් යුගල සුරකින්න"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"බෙදුම් තිරය භාවිතා කිරීමට තවත් යෙදුමක් තට්ටු කරන්න"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"බෙදුම් තිරය භාවිත කිරීමට වෙනත් යෙදුමක් තෝරා ගන්න"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"අවලංගු කරන්න"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"බෙදීම් තිර තේරීමෙන් පිටවන්න"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"බෙදීම් තිරය භාවිතා කිරීමට වෙනත් යෙදුමක් තෝරා ගන්න"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"මෙම ක්රියාව යෙදුම හෝ ඔබේ සංවිධානය මගින් ඉඩ නොදේ"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 278ad0e..12a00a3 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predpovedaná aplikácia: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"Otočte zariadenie"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"Otočte zariadenie a dokončite tak návod, ako navigovať gestami"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Musíte potiahnuť úplne z pravého alebo ľavého okraja"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"Musíte potiahnuť úplne z pravého alebo ľavého okraja."</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"Musíte potiahnuť z pravého alebo ľavého okraja do stredu obrazovky a potom uvoľniť"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"Naučili ste sa prejsť späť potiahnutím sprava. V ďalšom kroku sa naučíte prepínať aplikácie."</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"Dokončili ste gesto na prechod späť. V ďalšom kroku sa naučíte, ako prepínať aplikácie."</string>
@@ -58,8 +59,8 @@
<string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Na poslednú obrazovku prejdete potiahnutím z ľavého alebo pravého okraja do stredu obrazovky."</string>
<string name="back_gesture_spoken_intro_subtitle" msgid="2162043199263088592">"Na poslednú obrazovku sa vrátite potiahnutím dvoma prstami z ľavého alebo pravého okraja do stredu obrazovky."</string>
<string name="back_gesture_tutorial_title" msgid="1944737946101059789">"Prechod späť"</string>
- <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Potiahnite z ľavého alebo pravého okraja do stredu obrazovky"</string>
- <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Musíte potiahnuť nahor z dolného okraja obrazovky"</string>
+ <string name="back_gesture_tutorial_subtitle" msgid="6639993416000920142">"Potiahnite z ľavého alebo pravého okraja do stredu obrazovky."</string>
+ <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="4816365433160895458">"Musíte potiahnuť nahor z dolného okraja obrazovky."</string>
<string name="home_gesture_feedback_overview_detected" msgid="5177627157303895077">"Pred uvoľnením nesmiete zastať"</string>
<string name="home_gesture_feedback_wrong_swipe_direction" msgid="8328465201424027148">"Musíte potiahnuť priamo nahor"</string>
<string name="home_gesture_feedback_complete_with_follow_up" msgid="8766981412895888417">"Dokončili ste gesto prechodu na plochu. Teraz sa naučíte, ako sa vrátiť späť."</string>
@@ -68,18 +69,18 @@
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Potiahnite nahor zdola obrazovky. Týmto gestom sa vždy vrátite na plochu."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"Postiahnite dvoma prstami z dolnej časti obrazovky. Týmto gestom sa vždy vrátite na plochu."</string>
<string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Prechod na plochu"</string>
- <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Potiahnite z dolnej časti obrazovky nahor"</string>
+ <string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Potiahnite z dolnej časti obrazovky nahor."</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Skvelé!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Musíte potiahnuť nahor z dolného okraja obrazovky"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"Skúste okno pred uvoľnením podržať dlhšie"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Musite potiahnuť priamo nahor a potom zastať"</string>
- <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste sa používať gestá. Gestá môžete vypnúť v nastaveniach."</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"Musite potiahnuť priamo nahor a potom zastať."</string>
+ <string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"Naučili ste sa používať gestá. Gestá môžete vypnúť v Nastaveniach."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"Dokončili ste gesto na prepnutie aplikácií"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prepínanie aplikácií potiahnutím"</string>
<string name="overview_gesture_intro_subtitle" msgid="4968091015637850859">"Aplikácie môžete prepínať potiahnutím obrazovky zdola nahor, pridržaním a následným uvoľnením."</string>
<string name="overview_gesture_spoken_intro_subtitle" msgid="3853371838260201751">"Aplikácie prepnete potiahnutím dvoma prstami z dolnej časti obrazovky, ich pridržaním a uvoľnením."</string>
<string name="overview_gesture_tutorial_title" msgid="4125835002668708720">"Prepnutie aplikácií"</string>
- <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Potiahnite nahor z dolného okraja obrazovky, pridržte a uvoľnite"</string>
+ <string name="overview_gesture_tutorial_subtitle" msgid="5253549754058973071">"Potiahnite nahor z dolného okraja obrazovky, pridržte a uvoľnite."</string>
<string name="overview_gesture_tutorial_success" msgid="1910267697807973076">"Výborne"</string>
<string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Hotovo"</string>
<string name="gesture_tutorial_action_button_label" msgid="6249846312991332122">"Hotovo"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Uložiť pár aplikácií"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Obrazovku rozdelíte klepnutím na inú aplikáciu"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Na použitie rozdelenej obrazovky vyberte ďalšiu aplikáciu"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Zrušiť"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ukončite výber rozdelenej obrazovky"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Na použitie rozd. obrazovky vyberte inú aplikáciu"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 0c201b4..912ef83 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Počisti vse"</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index cd5725d..2be5e2b 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Ruaj çiftin e aplikacioneve"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Trokit një apl. tjetër; përdor ekranin e ndarë"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Zgjidh një aplikacion tjetër për të përdorur ekranin e ndarë"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Anulo"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Dil nga zgjedhja e ekranit të ndarë"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Zgjidh një aplikacion tjetër për të përdorur ekranin e ndarë"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Ky veprim nuk lejohet nga aplikacioni ose organizata jote"</string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 708406b..88e58f3 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Обриши све"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Сачувај пар апликација"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Додирните другу апликацију за подељени екран"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Одаберите другу апликацију да бисте користили подељени екран"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Откажи"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Излазак из бирања подељеног екрана"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Одаберите другу апликацију за подељени екран"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Апликација или организација не дозвољавају ову радњу"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index ef0ef76..bc58772 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Spara app-par"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tryck på en annan app för att använda delad skärm"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Välj en annan app för att använda delad skärm"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Avbryt"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Avsluta val av delad skärm"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Välj en annan app för att använda delad skärm"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisationen tillåter inte den här åtgärden"</string>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 63b5922..f2935b5 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Ondoa zote"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Hifadhi jozi ya programu"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Gusa programu nyingine ili utumie kipengele cha kugawa skrini"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Chagua programu nyingine ili utumie hali ya kugawa skrini"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Acha"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Ondoka kwenye hali ya skrini iliyogawanywa"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Chagua programu nyingine ili utumie hali ya kugawa skrini"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Kitendo hiki hakiruhusiwi na programu au shirika lako"</string>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 5159245..28585a4 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
@@ -72,7 +73,7 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"அருமை!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"விடுவிப்பதற்கு முன்பாக நீண்டநேரம் சாளரத்தை அழுத்திப் பிடித்திருங்கள்"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"மேல்நோக்கி நேராக ஸ்வைப் செய்தபிறகு இடைநிறுத்துவதை உறுதிசெய்துகொள்ளுங்கள்"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"மேல்நோக்கி நேராக ஸ்வைப் செய்தபிறகு சற்றுநேரம் அழுத்திபடியே வைத்திருங்கள்"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"சைகைகளை எப்படி உபயோகிப்பது என்று கற்றுக்கொண்டீர்கள். சைகைகளை முடக்க அமைப்புகளுக்குச் செல்லுங்கள்."</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"ஆப்ஸுக்கிடையே மாறும் சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"ஆப்ஸுக்கிடையே மாற ஸ்வைப் செய்யுங்கள்"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ஆப்ஸ் ஜோடியைச் சேமி"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"திரைப் பிரிப்பைப் பயன்படுத்த வேறு ஆப்ஸைத் தட்டவும்"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"திரைப் பிரிப்பைப் பயன்படுத்த வேறு ஆப்ஸைத் தேர்வுசெய்யுங்கள்"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"ரத்துசெய்"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"திரைப் பிரிப்பு தேர்வில் இருந்து வெளியேறும்"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"திரைப் பிரிப்பை பயன்படுத்த வேறு ஆப்ஸை தேர்வுசெய்க"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ஆப்ஸோ உங்கள் நிறுவனமோ இந்த செயலை அனுமதிப்பதில்லை"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 8c8e688..6255cfc 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 2c5f9a6..f47e34d 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"ล้างทั้งหมด"</string>
@@ -47,7 +48,7 @@
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"แอปที่คาดว่าจะใช้: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"หมุนอุปกรณ์ของคุณ"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"โปรดหมุนอุปกรณ์เพื่อทำตามบทแนะนำการนำทางด้วยท่าทางสัมผัสให้เสร็จสมบูรณ์"</string>
- <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ตรวจสอบว่าปัดจากขอบด้านขวาสุดหรือซ้ายสุด"</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"ปัดจากขอบด้านขวาสุดหรือซ้ายสุด"</string>
<string name="back_gesture_feedback_cancelled" msgid="762621530959111290">"ตรวจสอบว่าปัดจากขอบด้านขวาหรือซ้ายไปตรงกลางหน้าจอ แล้วยกนิ้วขึ้น"</string>
<string name="back_gesture_feedback_complete_with_overview_follow_up" msgid="9176400654037014471">"คุณรู้วิธีปัดจากด้านขวาเพื่อย้อนกลับแล้ว ต่อไปดูวิธีสลับแอป"</string>
<string name="back_gesture_feedback_complete_with_follow_up" msgid="8653374779579748392">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว ต่อไปดูวิธีสลับแอป"</string>
@@ -72,7 +73,7 @@
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"เก่งมาก"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"ปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string>
<string name="overview_gesture_feedback_home_detected" msgid="663432226180397138">"ลองแตะหน้าต่างค้างไว้นานขึ้นก่อนปล่อยนิ้ว"</string>
- <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ตรวจสอบว่าปัดขึ้นในแนวตรง แล้วหยุดชั่วคราว"</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="1191055451018584958">"ปัดขึ้นในแนวตรง แล้วหยุดชั่วคราว"</string>
<string name="overview_gesture_feedback_complete_with_follow_up" msgid="3544611727467765026">"คุณรู้วิธีใช้ท่าทางสัมผัสแล้ว หากต้องการปิดท่าทางสัมผัส ให้ไปที่การตั้งค่า"</string>
<string name="overview_gesture_feedback_complete_without_follow_up" msgid="2903050864432331629">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว"</string>
<string name="overview_gesture_intro_title" msgid="2902054412868489378">"ปัดเพื่อสลับแอป"</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 88ebb4a..582b756 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"I-clear lahat"</string>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 6117bfd..9842ebc 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Tümünü temizle"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 44170e9..20564c3 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистити все"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Зберегти пару"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Щоб розділити екран, виберіть ще один додаток."</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Щоб розділити екран, виберіть ще один додаток."</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Скасувати"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Вийти з режиму розділення екрана"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Щоб розділити екран, виберіть ще один додаток."</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Ця дія заборонена додатком або адміністратором організації"</string>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 89cfbbb..7c3a41f 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"ایپس کے جوڑے کو محفوظ کریں"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"اسپلٹ اسکرین کا استعمال کرنے کیلئے دوسری ایپ پر تھپتھپائیں"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"اسپلٹ اسکرین کے استعمال کیلئے دوسری ایپ منتخب کریں"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"منسوخ کریں"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"اسپلٹ اسکرین کے انتخاب سے باہر نکلیں"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"اسپلٹ اسکرین کے استعمال کیلئے دوسری ایپ منتخب کریں"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"ایپ یا آپ کی تنظیم کی جانب سے اس کارروائی کی اجازت نہیں ہے"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index e3c7727..ac02413 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 8844fe9..4c11957 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Xóa tất cả"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index b52d33b..bdc99df 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"保存应用组合"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"另外选择一个应用才可使用分屏模式"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"取消"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"退出分屏选择模式"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"另外选择一个应用才可使用分屏模式"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"该应用或您所在的单位不允许执行此操作"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index a048848..abdb91c 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index a27f2c5..91da74e 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"儲存應用程式配對"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"輕觸另一個應用程式即可使用分割畫面"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"選擇要在分割畫面中使用的另一個應用程式"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"取消"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"退出分割畫面選擇器"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"必須選擇另一個應用程式才能使用分割畫面"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"這個應用程式或貴機構不允許執行這個動作"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index b5f6abc..718400d 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -22,6 +22,7 @@
<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>
+ <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>
<string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
@@ -99,8 +100,7 @@
<string name="action_save_app_pair" msgid="5974823919237645229">"Londoloza ukubhangqa i-app"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Thepha enye i-app ukuze usebenzise isikrini sokuhlukanisa"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Khetha enye i-app ukuze usebenzise ukuhlukanisa isikrini"</string>
- <!-- no translation found for toast_split_select_app_cancel (1939025102486630426) -->
- <skip />
+ <string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Khansela"</string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Phuma ekukhetheni ukuhlukaniswa kwesikrini"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Khetha enye i-app ukuze usebenzise ukuhlukanisa isikrini"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 867ce17..d981882 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -41,6 +41,8 @@
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<!-- The icon size for the focused task, placed in center of touch target -->
<dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
+ <!-- The splash icon size on in Overview when a task is settled in the list -->
+ <dimen name="task_thumbnail_splash_icon_size">52dp</dimen>
<!-- The border width shown when task is hovered -->
<dimen name="task_hover_border_width">4dp</dimen>
<!-- The space under the focused task icon -->
@@ -374,6 +376,7 @@
<dimen name="transient_taskbar_stash_spring_velocity_dp_per_s">400dp</dimen>
<dimen name="taskbar_tooltip_vertical_padding">8dp</dimen>
<dimen name="taskbar_tooltip_horizontal_padding">16dp</dimen>
+ <dimen name="taskbar_tooltip_y_offset">4dp</dimen>
<!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
<dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index d973149..e940553 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
+import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
@@ -196,6 +197,7 @@
if (skipFirstFrame) {
// Because t=0 has the app icon in its original spot, we can skip the
// first frame and have the same movement one frame earlier.
+ Log.d("b/311077782", "LauncherAnimationRunner.setAnimation");
mAnimator.setCurrentPlayTime(
Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 189deda..f7da34a 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -30,7 +30,7 @@
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.views.DesktopTaskView
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import java.util.function.Consumer
/** Manage recents related operations with desktop tasks */
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 644705b..e31b1d4 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -36,10 +36,10 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.util.HashSet;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
* Controls the visibility of the workspace and the resumed / paused state when desktop mode
@@ -51,6 +51,7 @@
private static final boolean DEBUG = false;
private final Launcher mLauncher;
private final Set<DesktopVisibilityListener> mDesktopVisibilityListeners = new HashSet<>();
+ private final Set<TaskbarDesktopModeListener> mTaskbarDesktopModeListeners = new HashSet<>();
private int mVisibleDesktopTasksCount;
private boolean mInOverviewState;
@@ -110,6 +111,16 @@
mDesktopVisibilityListeners.remove(listener);
}
+ /** Registers a listener for Taskbar changes in Desktop Mode. */
+ public void registerTaskbarDesktopModeListener(TaskbarDesktopModeListener listener) {
+ mTaskbarDesktopModeListeners.add(listener);
+ }
+
+ /** Removes a previously registered listener for Taskbar changes in Desktop Mode. */
+ public void unregisterTaskbarDesktopModeListener(TaskbarDesktopModeListener listener) {
+ mTaskbarDesktopModeListeners.remove(listener);
+ }
+
/**
* Sets the number of desktop windows that are visible and updates launcher visibility based on
* it.
@@ -201,6 +212,16 @@
DisplayController.handleInfoChangeForDesktopMode(mLauncher);
}
+ private void notifyTaskbarDesktopModeListeners(boolean doesAnyTaskRequireTaskbarRounding) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyTaskbarDesktopModeListeners: doesAnyTaskRequireTaskbarRounding="
+ + doesAnyTaskRequireTaskbarRounding);
+ }
+ for (TaskbarDesktopModeListener listener : mTaskbarDesktopModeListeners) {
+ listener.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding);
+ }
+ }
+
/**
* TODO: b/333533253 - Remove after flag rollout
*/
@@ -377,7 +398,29 @@
@Override
public void onStashedChanged(int displayId, boolean stashed) {
- Log.w(TAG, "IDesktopTaskListener: onStashedChanged is deprecated");
+ Log.w(TAG, "DesktopTaskListenerImpl: onStashedChanged is deprecated");
}
+
+ @Override
+ public void onTaskbarCornerRoundingUpdate(boolean doesAnyTaskRequireTaskbarRounding) {
+ MAIN_EXECUTOR.execute(() -> {
+ if (mController != null && DesktopModeStatus.useRoundedCorners()) {
+ Log.d(TAG, "DesktopTaskListenerImpl: doesAnyTaskRequireTaskbarRounding= "
+ + doesAnyTaskRequireTaskbarRounding);
+ mController.notifyTaskbarDesktopModeListeners(
+ doesAnyTaskRequireTaskbarRounding);
+ }
+ });
+ }
+ }
+
+ /** A listener for Taskbar in Desktop Mode. */
+ public interface TaskbarDesktopModeListener {
+ /**
+ * Callback for when task is resized in desktop mode.
+ *
+ * @param doesAnyTaskRequireTaskbarRounding whether task requires taskbar corner roundness.
+ */
+ void onTaskbarCornerRoundingUpdate(boolean doesAnyTaskRequireTaskbarRounding);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 60fb094..96a6d28 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -248,7 +248,7 @@
}
mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, isVisible);
- if (fromInit) {
+ if (fromInit || mControllers == null) {
duration = 0;
}
return mTaskbarLauncherStateController.applyState(duration, startAnimation);
@@ -289,7 +289,12 @@
}
public boolean isDraggingItem() {
- return mControllers.taskbarDragController.isDragging();
+ boolean bubblesDragging = false;
+ if (mControllers.bubbleControllers.isPresent()) {
+ bubblesDragging =
+ mControllers.bubbleControllers.get().bubbleDragController.isDragging();
+ }
+ return mControllers.taskbarDragController.isDragging() || bubblesDragging;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index f61840a..a979d58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -63,7 +63,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.RotateDrawable;
@@ -74,8 +73,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
-import android.view.View.OnClickListener;
-import android.view.View.OnHoverListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
@@ -106,7 +103,6 @@
import com.android.systemui.shared.navigationbar.KeyButtonRipple;
import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton;
-import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -304,8 +300,13 @@
.get(ALPHA_INDEX_SMALL_SCREEN),
flags -> (flags & FLAG_SMALL_SCREEN) == 0));
- mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
- .getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0));
+ if (!mContext.isPhoneMode()) {
+ mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
+ .getKeyguardBgTaskbar(), flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0));
+ }
+
+ // Start at 1 because relevant flags are unset at init.
+ mOnBackgroundNavButtonColorOverrideMultiplier.value = 1;
// Force nav buttons (specifically back button) to be visible during setup wizard.
boolean isInSetup = !mContext.isUserSetupComplete();
@@ -317,39 +318,41 @@
// - IME is showing (add separate translation for IME)
// - VoiceInteractionWindow (assistant) is showing
// - Keyboard shortcuts helper is showing
- int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
- | FLAG_VOICE_INTERACTION_WINDOW_SHOWING | FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING;
- mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
- flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
- 1, 0));
- // Center nav buttons in new height for IME.
- float transForIme = (mContext.getDeviceProfile().taskbarHeight
- - mControllers.taskbarInsetsController.getTaskbarHeightForIme()) / 2f;
- // For gesture nav, nav buttons only show for IME anyway so keep them translated down.
- float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme;
- mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme,
- flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
- transForIme, defaultButtonTransY));
+ if (!mContext.isPhoneMode()) {
+ int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
+ | FLAG_VOICE_INTERACTION_WINDOW_SHOWING | FLAG_KEYBOARD_SHORTCUT_HELPER_SHOWING;
+ mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
+ flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
+ 1, 0));
+ // Center nav buttons in new height for IME.
+ float transForIme = (mContext.getDeviceProfile().taskbarHeight
+ - mControllers.taskbarInsetsController.getTaskbarHeightForIme()) / 2f;
+ // For gesture nav, nav buttons only show for IME anyway so keep them translated down.
+ float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme;
+ mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme,
+ flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
+ transForIme, defaultButtonTransY));
- // Start at 1 because relevant flags are unset at init.
- mOnBackgroundNavButtonColorOverrideMultiplier.value = 1;
- mPropertyHolders.add(new StatePropertyHolder(
- mOnBackgroundNavButtonColorOverrideMultiplier,
- flags -> (flags & FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED) == 0));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mOnBackgroundNavButtonColorOverrideMultiplier,
+ flags -> (flags & FLAGS_ON_BACKGROUND_COLOR_OVERRIDE_DISABLED) == 0));
- mPropertyHolders.add(new StatePropertyHolder(
- mSlideInViewVisibleNavButtonColorOverride,
- flags -> (flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mSlideInViewVisibleNavButtonColorOverride,
+ flags -> (flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0));
+ }
if (alwaysShowButtons) {
initButtons(mNavButtonContainer, mEndContextualContainer,
mControllers.navButtonController);
updateButtonLayoutSpacing();
- updateStateForFlag(FLAG_SMALL_SCREEN, mContext.isPhoneButtonNavMode());
+ updateStateForFlag(FLAG_SMALL_SCREEN, mContext.isPhoneMode());
- mPropertyHolders.add(new StatePropertyHolder(
- mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
- flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0));
+ if (!mContext.isPhoneMode()) {
+ mPropertyHolders.add(new StatePropertyHolder(
+ mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
+ flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0));
+ }
} else if (!mIsImeRenderingNavButtons) {
View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
mStartContextualContainer, mControllers.navButtonController, R.id.back);
@@ -711,7 +714,7 @@
private void applyState() {
int count = mPropertyHolders.size();
for (int i = 0; i < count; i++) {
- mPropertyHolders.get(i).setState(mState);
+ mPropertyHolders.get(i).setState(mState, mContext.isGestureNav());
}
}
@@ -1177,83 +1180,6 @@
}
}
- private class RotationButtonImpl implements RotationButton {
-
- private final ImageView mButton;
- private AnimatedVectorDrawable mImageDrawable;
-
- RotationButtonImpl(ImageView button) {
- mButton = button;
- }
-
- @Override
- public void setRotationButtonController(RotationButtonController rotationButtonController) {
- // TODO(b/187754252) UI polish, different icons based on light/dark context, etc
- mImageDrawable = (AnimatedVectorDrawable) mButton.getContext()
- .getDrawable(rotationButtonController.getIconResId());
- mButton.setImageDrawable(mImageDrawable);
- mButton.setContentDescription(mButton.getResources()
- .getString(R.string.accessibility_rotate_button));
- mImageDrawable.setCallback(mButton);
- }
-
- @Override
- public View getCurrentView() {
- return mButton;
- }
-
- @Override
- public boolean show() {
- mButton.setVisibility(View.VISIBLE);
- mState |= FLAG_ROTATION_BUTTON_VISIBLE;
- applyState();
- return true;
- }
-
- @Override
- public boolean hide() {
- mButton.setVisibility(View.GONE);
- mState &= ~FLAG_ROTATION_BUTTON_VISIBLE;
- applyState();
- return true;
- }
-
- @Override
- public boolean isVisible() {
- return mButton.getVisibility() == View.VISIBLE;
- }
-
- @Override
- public void updateIcon(int lightIconColor, int darkIconColor) {
- // TODO(b/187754252): UI Polish
- }
-
- @Override
- public void setOnClickListener(OnClickListener onClickListener) {
- mButton.setOnClickListener(onClickListener);
- }
-
- @Override
- public void setOnHoverListener(OnHoverListener onHoverListener) {
- mButton.setOnHoverListener(onHoverListener);
- }
-
- @Override
- public AnimatedVectorDrawable getImageDrawable() {
- return mImageDrawable;
- }
-
- @Override
- public void setDarkIntensity(float darkIntensity) {
- // TODO(b/187754252) UI polish
- }
-
- @Override
- public boolean acceptRotationProposal() {
- return mButton.isAttachedToWindow();
- }
- }
-
private static class StatePropertyHolder {
private final float mEnabledValue, mDisabledValue;
@@ -1284,13 +1210,16 @@
mAnimator = ObjectAnimator.ofFloat(target, property, enabledValue, disabledValue);
}
- public void setState(int flags) {
+ public void setState(int flags, boolean skipAnimation) {
boolean isEnabled = mEnableCondition.test(flags);
if (mIsEnabled != isEnabled) {
mIsEnabled = isEnabled;
mAnimator.cancel();
mAnimator.setFloatValues(mIsEnabled ? mEnabledValue : mDisabledValue);
mAnimator.start();
+ if (skipAnimation) {
+ mAnimator.end();
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
index 94e2244..caf3320 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
@@ -47,6 +47,7 @@
private final int[] mTmpArr = new int[2];
private @Nullable ObjectAnimator mColorChangeAnim;
+ private Boolean mIsRegionDark;
public StashedHandleView(Context context) {
this(context, null);
@@ -95,7 +96,11 @@
* @param animate Whether to animate the change, or apply it immediately.
*/
public void updateHandleColor(boolean isRegionDark, boolean animate) {
+ if (mIsRegionDark != null && mIsRegionDark == isRegionDark) {
+ return;
+ }
int newColor = isRegionDark ? mStashedHandleLightColor : mStashedHandleDarkColor;
+ mIsRegionDark = isRegionDark;
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index cd1eea2..0c1235c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -186,6 +186,7 @@
private final WindowManager mWindowManager;
private DeviceProfile mDeviceProfile;
private WindowManager.LayoutParams mWindowLayoutParams;
+ private WindowManager.LayoutParams mLastUpdatedLayoutParams;
private boolean mIsFullscreen;
// The size we should return to when we call setTaskbarWindowFullscreen(false)
private int mLastRequestedNonFullscreenSize;
@@ -347,8 +348,9 @@
new KeyboardQuickSwitchController(),
new TaskbarPinningController(this, () ->
DisplayController.isInDesktopMode(this)),
- bubbleControllersOptional);
-
+ bubbleControllersOptional,
+ new TaskbarDesktopModeController(
+ LauncherActivityInterface.INSTANCE::getDesktopVisibilityController));
mLauncherPrefs = LauncherPrefs.get(this);
}
@@ -442,6 +444,7 @@
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
mLastRequestedNonFullscreenSize = getDefaultTaskbarWindowSize();
mWindowLayoutParams = createAllWindowParams();
+ mLastUpdatedLayoutParams = new WindowManager.LayoutParams();
// Initialize controllers after all are constructed.
mControllers.init(sharedState);
@@ -502,6 +505,15 @@
}
/**
+ * Returns {@code true} iff bubble bar is enabled (but not necessarily visible /
+ * containing bubbles).
+ */
+ @Override
+ public boolean isBubbleBarEnabled() {
+ return getBubbleControllers() != null && BubbleBarController.isBubbleBarEnabled();
+ }
+
+ /**
* Returns if software keyboard is docked or input toolbar is placed at the taskbar area
*/
public boolean isImeDocked() {
@@ -1718,6 +1730,12 @@
void notifyUpdateLayoutParams() {
if (mDragLayer.isAttachedToWindow()) {
+ // Copy the current windowLayoutParams to mLastUpdatedLayoutParams and compare the diff.
+ // If there is no change, we will skip the call to updateViewLayout.
+ int changes = mLastUpdatedLayoutParams.copyFrom(mWindowLayoutParams);
+ if (changes == 0) {
+ return;
+ }
if (enableTaskbarNoRecreate()) {
mWindowManager.updateViewLayout(mDragLayer.getRootView(), mWindowLayoutParams);
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index ee64060..4ac7514 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -24,6 +24,7 @@
import android.graphics.Rect
import android.graphics.RectF
import com.android.app.animation.Interpolators
+import com.android.internal.policy.ScreenDecorationsUtils
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.mapRange
@@ -70,8 +71,8 @@
private var keyShadowDistance = 0f
private var bottomMargin = 0
- private val fullCornerRadius = context.cornerRadius.toFloat()
- private var cornerRadius = fullCornerRadius
+ private val fullCornerRadius: Float
+ private var cornerRadius = 0f
private var widthInsetPercentage = 0f
private val square = Path()
private val circle = Path()
@@ -101,7 +102,14 @@
shadowAlpha = LIGHT_THEME_SHADOW_ALPHA
}
- setCornerRoundness(DEFAULT_ROUNDNESS)
+ if (DisplayController.isInDesktopMode(context)) {
+ fullCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+ cornerRadius = fullCornerRadius
+ } else {
+ fullCornerRadius = context.cornerRadius.toFloat()
+ cornerRadius = fullCornerRadius
+ setCornerRoundness(MAX_ROUNDNESS)
+ }
}
fun updateStashedHandleWidth(context: TaskbarActivityContext, res: Resources) {
@@ -273,7 +281,7 @@
}
companion object {
- const val DEFAULT_ROUNDNESS = 1f
+ const val MAX_ROUNDNESS = 1f
private const val DARK_THEME_STROKE_ALPHA = 51
private const val LIGHT_THEME_STROKE_ALPHA = 41
private const val DARK_THEME_SHADOW_ALPHA = 51f
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index d94d9175..34ab9f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -25,6 +25,7 @@
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
+import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import java.io.PrintWriter;
@@ -64,6 +65,7 @@
public final KeyboardQuickSwitchController keyboardQuickSwitchController;
public final TaskbarPinningController taskbarPinningController;
public final Optional<BubbleControllers> bubbleControllers;
+ public final TaskbarDesktopModeController taskbarDesktopModeController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
@@ -111,7 +113,8 @@
TaskbarEduTooltipController taskbarEduTooltipController,
KeyboardQuickSwitchController keyboardQuickSwitchController,
TaskbarPinningController taskbarPinningController,
- Optional<BubbleControllers> bubbleControllers) {
+ Optional<BubbleControllers> bubbleControllers,
+ TaskbarDesktopModeController taskbarDesktopModeController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -138,6 +141,7 @@
this.keyboardQuickSwitchController = keyboardQuickSwitchController;
this.taskbarPinningController = taskbarPinningController;
this.bubbleControllers = bubbleControllers;
+ this.taskbarDesktopModeController = taskbarDesktopModeController;
}
/**
@@ -173,6 +177,7 @@
taskbarEduTooltipController.init(this);
keyboardQuickSwitchController.init(this);
taskbarPinningController.init(this, mSharedState);
+ taskbarDesktopModeController.init(this, mSharedState);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -188,7 +193,13 @@
taskbarDragLayerController, taskbarScrimViewController,
voiceInteractionWindowController
};
- mCornerRoundness.updateValue(TaskbarBackgroundRenderer.DEFAULT_ROUNDNESS);
+
+ if (DisplayController.isInDesktopMode(taskbarActivityContext)) {
+ mCornerRoundness.updateValue(taskbarDesktopModeController.getTaskbarCornerRoundness(
+ mSharedState.showCornerRadiusInDesktopMode));
+ } else {
+ mCornerRoundness.updateValue(TaskbarBackgroundRenderer.MAX_ROUNDNESS);
+ }
mAreAllControllersInitialized = true;
for (Runnable postInitCallback : mPostInitCallbacks) {
@@ -248,6 +259,7 @@
keyboardQuickSwitchController.onDestroy();
taskbarStashController.onDestroy();
bubbleControllers.ifPresent(controllers -> controllers.onDestroy());
+ taskbarDesktopModeController.onDestroy();
mControllersToLog = null;
mBackgroundRendererControllers = null;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
new file mode 100644
index 0000000..a376531
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
@@ -0,0 +1,54 @@
+/*
+ * 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
+
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statehandlers.DesktopVisibilityController.TaskbarDesktopModeListener
+import com.android.launcher3.taskbar.TaskbarBackgroundRenderer.Companion.MAX_ROUNDNESS
+
+/** Handles Taskbar in Desktop Windowing mode. */
+class TaskbarDesktopModeController(
+ private val desktopVisibilityControllerProvider: () -> DesktopVisibilityController?
+) : TaskbarDesktopModeListener {
+ private lateinit var taskbarControllers: TaskbarControllers
+ private lateinit var taskbarSharedState: TaskbarSharedState
+
+ private val desktopVisibilityController: DesktopVisibilityController?
+ get() = desktopVisibilityControllerProvider()
+
+ fun init(controllers: TaskbarControllers, sharedState: TaskbarSharedState) {
+ taskbarControllers = controllers
+ taskbarSharedState = sharedState
+ desktopVisibilityController?.registerTaskbarDesktopModeListener(this)
+ }
+
+ override fun onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding: Boolean) {
+ taskbarSharedState.showCornerRadiusInDesktopMode = doesAnyTaskRequireTaskbarRounding
+ val cornerRadius = getTaskbarCornerRoundness(doesAnyTaskRequireTaskbarRounding)
+ taskbarControllers.taskbarCornerRoundness.animateToValue(cornerRadius).start()
+ }
+
+ fun getTaskbarCornerRoundness(doesAnyTaskRequireTaskbarRounding: Boolean): Float {
+ return if (doesAnyTaskRequireTaskbarRounding) {
+ MAX_ROUNDNESS
+ } else {
+ 0f
+ }
+ }
+
+ fun onDestroy() = desktopVisibilityController?.unregisterTaskbarDesktopModeListener(this)
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
index 8c7879d..3bff31f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
@@ -50,6 +50,7 @@
private final View mHoverView;
private final ArrowTipView mHoverToolTipView;
private final String mToolTipText;
+ private final int mYOffset;
public TaskbarHoverToolTipController(TaskbarActivityContext activity, TaskbarView taskbarView,
View hoverView) {
@@ -79,6 +80,8 @@
mHoverToolTipView.findViewById(R.id.text).setPadding(horizontalPadding, verticalPadding,
horizontalPadding, verticalPadding);
mHoverToolTipView.setAlpha(0);
+ mYOffset = arrowContextWrapper.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_tooltip_y_offset);
AnimatorSet hoverOpenAnimator = new AnimatorSet();
ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 0f, 1f);
@@ -89,7 +92,7 @@
mHoverToolTipView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mHoverToolTipView.setPivotY(bottom);
- mHoverToolTipView.setY(mTaskbarView.getTop() - (bottom - top));
+ mHoverToolTipView.setY(mTaskbarView.getTop() - mYOffset - (bottom - top));
});
}
@@ -121,6 +124,6 @@
}
Rect iconViewBounds = Utilities.getViewBounds(mHoverView);
mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(),
- mTaskbarView.getTop(), /* shouldAutoClose= */ false);
+ mTaskbarView.getTop() - mYOffset, /* shouldAutoClose= */ false);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index ff1ea98..221504d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -56,7 +56,6 @@
import com.android.launcher3.util.Executors
import java.io.PrintWriter
import kotlin.jvm.optionals.getOrNull
-import kotlin.math.max
/** Handles the insets that Taskbar provides to underlying apps and the IME. */
class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTaskbarController {
@@ -106,7 +105,8 @@
}
fun onTaskbarOrBubblebarWindowHeightOrInsetsChanged() {
- val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
+ val taskbarStashController = controllers.taskbarStashController
+ val tappableHeight = taskbarStashController.tappableHeightToReportToApps
// We only report tappableElement height for unstashed, persistent taskbar,
// which is also when we draw the rounded corners above taskbar.
val insetsRoundedCornerFlag =
@@ -133,7 +133,7 @@
}
val bubbleControllers = controllers.bubbleControllers.getOrNull()
- val taskbarTouchableHeight = controllers.taskbarStashController.touchableHeight
+ val taskbarTouchableHeight = taskbarStashController.touchableHeight
val bubblesTouchableHeight =
bubbleControllers?.bubbleStashController?.getTouchableHeight() ?: 0
// reset touch bounds
@@ -147,12 +147,10 @@
defaultTouchableRegion.addBoundsToRegion(bubbleBarViewController.bubbleBarBounds)
}
}
- val taskbarUIController = controllers.uiController as? LauncherTaskbarUIController
- if (taskbarUIController?.isOnHome != true) {
- // only add the bars touch region if not on home
- val touchableHeight = max(taskbarTouchableHeight, bubblesTouchableHeight)
+ if (taskbarStashController.isInApp || taskbarStashController.isInOverview) {
+ // only add the taskbar touch region if not on home
val bottom = windowLayoutParams.height
- val top = bottom - touchableHeight
+ val top = bottom - taskbarTouchableHeight
val right = context.deviceProfile.widthPx
defaultTouchableRegion.addBoundsToRegion(Rect(/* left= */ 0, top, right, bottom))
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 5c3add2..63fae8c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
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;
@@ -47,6 +48,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
@@ -146,6 +148,7 @@
private MultiProperty mIconAlphaForHome;
private QuickstepLauncher mLauncher;
+ private boolean mIsDestroyed = false;
private Integer mPrevState;
private int mState;
private LauncherState mLauncherState = LauncherState.NORMAL;
@@ -244,7 +247,9 @@
resetIconAlignment();
- mLauncher.getStateManager().addStateListener(mStateListener);
+ if (!mControllers.taskbarActivityContext.isPhoneMode()) {
+ mLauncher.getStateManager().addStateListener(mStateListener);
+ }
mLauncherState = launcher.getStateManager().getState();
updateStateForSysuiFlags(sysuiStateFlags, /*applyState*/ false);
@@ -256,6 +261,7 @@
}
public void onDestroy() {
+ mIsDestroyed = true;
mCanSyncViews = false;
mIconAlignment.finishAnimation();
@@ -349,8 +355,10 @@
// 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.
- boolean isTaskbarHidden = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_DEVICE_DREAMING)
+ // 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);
@@ -407,7 +415,7 @@
}
public Animator applyState(long duration, boolean start) {
- if (mControllers.taskbarActivityContext.isDestroyed()) {
+ if (mIsDestroyed || mControllers.taskbarActivityContext.isPhoneMode()) {
return null;
}
Animator animator = null;
@@ -584,6 +592,12 @@
float cornerRoundness = isInLauncher ? 0 : 1;
+ if (DisplayController.isInDesktopMode(mLauncher) && mControllers.getSharedState() != null) {
+ cornerRoundness =
+ mControllers.taskbarDesktopModeController.getTaskbarCornerRoundness(
+ mControllers.getSharedState().showCornerRadiusInDesktopMode);
+ }
+
// Don't animate if corner roundness has reached desired value.
if (mTaskbarCornerRoundness.isAnimating()
|| mTaskbarCornerRoundness.value != cornerRoundness) {
@@ -753,7 +767,7 @@
}
private void updateIconAlphaForHome(float alpha) {
- if (mControllers.taskbarActivityContext.isDestroyed()) {
+ if (mIsDestroyed) {
return;
}
mIconAlphaForHome.setValue(alpha);
@@ -856,7 +870,8 @@
"%s\tmTaskbarBackgroundAlpha=%.2f", prefix, mTaskbarBackgroundAlpha.value));
pw.println(String.format(
"%s\tmIconAlphaForHome=%.2f", prefix, mIconAlphaForHome.getValue()));
- pw.println(String.format("%s\tmPrevState=%s", prefix, getStateString(mPrevState)));
+ pw.println(String.format("%s\tmPrevState=%s", prefix,
+ mPrevState == null ? null : getStateString(mPrevState)));
pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState)));
pw.println(String.format("%s\tmLauncherState=%s", prefix, mLauncherState));
pw.println(String.format(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index f411e79..8c87fa6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -459,10 +459,12 @@
+ " [dp != null (i.e. mUserUnlocked)]=" + (dp != null)
+ " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent));
- if (!isTaskbarEnabled) {
+ if (!isTaskbarEnabled || !isLargeScreenTaskbar) {
SystemUiProxy.INSTANCE.get(mContext)
.notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
- return;
+ if (!isTaskbarEnabled) {
+ return;
+ }
}
if (enableTaskbarNoRecreate() || mTaskbarActivityContext == null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index 77bd35f..729cbe9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -102,5 +102,8 @@
// To track if taskbar was stashed / unstashed between configuration changes (which recreates
// the task bar).
- public Boolean taskbarWasStashedAuto = true;
+ public boolean taskbarWasStashedAuto = true;
+
+ // should show corner radius on persistent taskbar when in desktop mode.
+ public boolean showCornerRadiusInDesktopMode = false;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 8a20131..56f88d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -424,6 +424,11 @@
return hasAnyFlag(FLAGS_IN_APP);
}
+ /** Returns whether the taskbar is currently in overview screen. */
+ public boolean isInOverview() {
+ return hasAnyFlag(FLAG_IN_OVERVIEW);
+ }
+
/**
* Returns the height that taskbar will be touchable.
*/
@@ -1146,7 +1151,8 @@
*/
public void setUpTaskbarSystemAction(boolean visible) {
UI_HELPER_EXECUTOR.execute(() -> {
- if (!visible || !DisplayController.isTransientTaskbar(mActivity)) {
+ if (!visible || !DisplayController.isTransientTaskbar(mActivity)
+ || mActivity.isPhoneMode()) {
mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR);
mIsTaskbarSystemActionRegistered = false;
return;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index fc76972..54a7fdc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -108,9 +108,15 @@
private long mAllAppsButtonTouchDelayMs;
private boolean mAllAppsTouchTriggered;
- // Only non-null when device supports having an All Apps button.
+ // Only non-null when device supports having an All Apps button, or Recent Apps.
private @Nullable IconButtonView mTaskbarDivider;
+ /**
+ * Whether the divider is between Hotseat icons and Recents,
+ * instead of between All Apps button and Hotseat.
+ */
+ private boolean mAddedDividerForRecents;
+
private final View mQsb;
private final float mTransientTaskbarMinWidth;
@@ -340,7 +346,7 @@
protected void updateHotseatItems(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
int nextViewIndex = 0;
int numViewsAnimated = 0;
- boolean addedDividerForRecents = false;
+ mAddedDividerForRecents = false;
if (mAllAppsButton != null) {
removeView(mAllAppsButton);
@@ -435,7 +441,7 @@
if (mTaskbarDivider != null && !recentTasks.isEmpty()) {
addView(mTaskbarDivider, nextViewIndex++);
- addedDividerForRecents = true;
+ mAddedDividerForRecents = true;
}
// Add Recent/Running icons.
@@ -499,10 +505,10 @@
}
if (mAllAppsButton != null) {
- addView(mAllAppsButton, mIsRtl ? getChildCount() : 0);
+ addView(mAllAppsButton, mIsRtl ? hotseatItemInfos.length : 0);
// If there are no recent tasks, add divider after All Apps (unless it's the only view).
- if (!addedDividerForRecents && mTaskbarDivider != null && getChildCount() > 1) {
+ if (!mAddedDividerForRecents && mTaskbarDivider != null && getChildCount() > 1) {
addView(mTaskbarDivider, mIsRtl ? (getChildCount() - 1) : 1);
}
}
@@ -734,6 +740,14 @@
}
/**
+ * Returns whether the divider is between Hotseat icons and Recents,
+ * instead of between All Apps button and Hotseat.
+ */
+ public boolean isDividerForRecents() {
+ return mAddedDividerForRecents;
+ }
+
+ /**
* Returns the QSB in the taskbar.
*/
public View getQsb() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index b8b85d1..27c1d9c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -100,6 +100,12 @@
public static final int ALPHA_INDEX_BUBBLE_BAR = 7;
private static final int NUM_ALPHA_CHANNELS = 8;
+ /** Only used for animation purposes, to position the divider between two item indices. */
+ public static final float DIVIDER_VIEW_POSITION_OFFSET = 0.5f;
+
+ /** Used if an unexpected edge case is hit in {@link #getPositionInHotseat}. */
+ private static final float ERROR_POSITION_IN_HOTSEAT_NOT_FOUND = -100;
+
private static boolean sEnableModelLoadingForTests = true;
private final TaskbarActivityContext mActivity;
@@ -144,6 +150,7 @@
private Runnable mOnControllerPreCreateCallback = NO_OP;
// Stored here as signals to determine if the mIconAlignController needs to be recreated.
+ private boolean mIsIconAlignedWithHotseat;
private boolean mIsHotseatIconOnTopWhenAligned;
private boolean mIsStashed;
@@ -212,8 +219,8 @@
if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
// This gets modified in NavbarButtonsViewController, but the initial value it reads
// may be incorrect since it's state gets destroyed on taskbar recreate, so reset here
- mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN)
- .animateToValue(mActivity.isPhoneButtonNavMode() ? 0 : 1).start();
+ mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN).setValue(
+ mActivity.isPhoneMode() ? 0 : 1);
}
if (enableTaskbarPinning()) {
mTaskbarView.addOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
@@ -681,15 +688,17 @@
mIconAlignControllerLazy = null;
return;
}
-
boolean isHotseatIconOnTopWhenAligned =
mControllers.uiController.isHotseatIconOnTopWhenAligned();
+ boolean isIconAlignedWithHotseat = mControllers.uiController.isIconAlignedWithHotseat();
boolean isStashed = mControllers.taskbarStashController.isStashed();
- // Re-create animation when mIsHotseatIconOnTopWhenAligned or mIsStashed changes.
+ // Re-create animation when any of these values change.
if (mIconAlignControllerLazy == null
|| mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned
+ || mIsIconAlignedWithHotseat != isIconAlignedWithHotseat
|| mIsStashed != isStashed) {
mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
+ mIsIconAlignedWithHotseat = isIconAlignedWithHotseat;
mIsStashed = isStashed;
mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
@@ -741,17 +750,22 @@
? mTransientTaskbarDp.taskbarBottomMargin
: mPersistentTaskbarDp.taskbarBottomMargin;
+ int firstRecentTaskIndex = -1;
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView();
boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerView();
+ boolean isRecentTask = child.getTag() instanceof GroupTask;
+ // TODO(b/343522351): show recents on the home screen.
+ final boolean isRecentsInHotseat = false;
if (!mIsHotseatIconOnTopWhenAligned) {
// When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController
// plays iconAlignment to 1 really fast, therefore moving the fading towards the end
// to avoid icons disappearing rather than fading out visually.
setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
} else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())
- || (isTaskbarDividerView && enableTaskbarPinning())) {
+ || (isTaskbarDividerView && enableTaskbarPinning())
+ || (isRecentTask && !isRecentsInHotseat)) {
if (!isToHome
&& mIsHotseatIconOnTopWhenAligned
&& mIsStashed) {
@@ -812,25 +826,17 @@
continue;
}
- float positionInHotseat;
- if (isAllAppsButton) {
- // Note that there is no All Apps button in the hotseat,
- // this position is only used as its convenient for animation purposes.
- positionInHotseat = Utilities.isRtl(child.getResources())
- ? taskbarDp.numShownHotseatIcons
- : -1;
- } else if (isTaskbarDividerView) {
- // Note that there is no taskbar divider view in the hotseat,
- // this position is only used as its convenient for animation purposes.
- positionInHotseat = Utilities.isRtl(child.getResources())
- ? taskbarDp.numShownHotseatIcons - 0.5f
- : -0.5f;
- } else if (child.getTag() instanceof ItemInfo) {
- positionInHotseat = ((ItemInfo) child.getTag()).screenId;
- } else {
- Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
- continue;
+ int recentTaskIndex = -1;
+ if (isRecentTask) {
+ if (firstRecentTaskIndex < 0) {
+ firstRecentTaskIndex = i;
+ }
+ recentTaskIndex = i - firstRecentTaskIndex;
}
+ float positionInHotseat = getPositionInHotseat(taskbarDp.numShownHotseatIcons, child,
+ mIsRtl, isAllAppsButton, isTaskbarDividerView,
+ mTaskbarView.isDividerForRecents(), recentTaskIndex);
+ if (positionInHotseat == ERROR_POSITION_IN_HOTSEAT_NOT_FOUND) continue;
float hotseatAdjustedBorderSpace =
launcherDp.getHotseatAdjustedBorderSpaceForBubbleBar(child.getContext());
@@ -866,6 +872,58 @@
return controller;
}
+ /**
+ * Returns the index of the given child relative to its position in hotseat.
+ * Examples:
+ * -1 is the item before the first hotseat item.
+ * -0.5 is between those (e.g. for the divider).
+ * {@link #ERROR_POSITION_IN_HOTSEAT_NOT_FOUND} if there's no calculation relative to hotseat.
+ */
+ @VisibleForTesting
+ float getPositionInHotseat(int numShownHotseatIcons, View child, boolean isRtl,
+ boolean isAllAppsButton, boolean isTaskbarDividerView, boolean isDividerForRecents,
+ int recentTaskIndex) {
+ float positionInHotseat;
+ // Note that there is no All Apps button in the hotseat,
+ // this position is only used as it's convenient for animation purposes.
+ float allAppsButtonPositionInHotseat = isRtl
+ // Right after all hotseat items.
+ // [HHHHHH]|[>A<]
+ ? numShownHotseatIcons
+ // Right before all hotseat items.
+ // [>A<]|[HHHHHH]
+ : -1;
+ // Note that there are no recent tasks in the hotseat,
+ // this position is only used as it's convenient for animation purposes.
+ float firstRecentTaskPositionInHotseat = isRtl
+ // After all hotseat icons and All Apps button.
+ // [HHHHHH][A]|[>R<R]
+ ? numShownHotseatIcons + 1
+ // Right after all hotseat items.
+ // [A][HHHHHH]|[>R<R]
+ : numShownHotseatIcons;
+ if (isAllAppsButton) {
+ positionInHotseat = allAppsButtonPositionInHotseat;
+ } else if (isTaskbarDividerView) {
+ // Note that there is no taskbar divider view in the hotseat,
+ // this position is only used as it's convenient for animation purposes.
+ float relativePosition = isDividerForRecents
+ ? firstRecentTaskPositionInHotseat
+ : allAppsButtonPositionInHotseat;
+ positionInHotseat = relativePosition > 0
+ ? relativePosition - DIVIDER_VIEW_POSITION_OFFSET
+ : relativePosition + DIVIDER_VIEW_POSITION_OFFSET;
+ } else if (child.getTag() instanceof ItemInfo) {
+ positionInHotseat = ((ItemInfo) child.getTag()).screenId;
+ } else if (recentTaskIndex >= 0) {
+ positionInHotseat = firstRecentTaskPositionInHotseat + recentTaskIndex;
+ } else {
+ Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
+ return ERROR_POSITION_IN_HOTSEAT_NOT_FOUND;
+ }
+ return positionInHotseat;
+ }
+
private boolean bubbleBarHasBubbles() {
return mControllers.bubbleControllers.isPresent()
&& mControllers.bubbleControllers.get().bubbleBarViewController.hasBubbles();
@@ -950,7 +1008,6 @@
"ALPHA_INDEX_RECENTS_DISABLED",
"ALPHA_INDEX_NOTIFICATION_EXPANDED",
"ALPHA_INDEX_ASSISTANT_INVOKED",
- "ALPHA_INDEX_IME_BUTTON_NAV",
"ALPHA_INDEX_SMALL_SCREEN");
mModelCallbacks.dumpLogs(prefix + "\t", pw);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index a36d5f0..cdd3e13 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -496,6 +496,11 @@
() -> mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation));
}
+ /** Notifies WMShell to show the expanded view. */
+ void showExpandedView() {
+ mSystemUiProxy.showExpandedView();
+ }
+
//
// Loading data for the bubbles
//
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 1f0851f..c458936 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -244,7 +244,7 @@
if (mIsBarExpanded && mSelectedBubbleView != null) {
mSelectedBubbleView.markSeen();
}
- updateWidth();
+ updateLayoutParams();
},
/* onUpdate= */ animator -> {
updateBubblesLayoutProperties(mBubbleBarLocation);
@@ -733,7 +733,7 @@
@Override
public void onAnimationEnd() {
- updateWidth();
+ updateLayoutParams();
mBubbleAnimator = null;
}
@@ -791,7 +791,7 @@
@Override
public void onAnimationEnd() {
removeView(removedBubble);
- updateWidth();
+ updateLayoutParams();
mBubbleAnimator = null;
if (onEndRunnable != null) {
onEndRunnable.run();
@@ -823,7 +823,7 @@
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
- updateWidth();
+ updateLayoutParams();
updateBubbleAccessibilityStates();
updateContentDescription();
}
@@ -887,7 +887,7 @@
mSelectedBubbleView = null;
mBubbleBarBackground.showArrow(false);
}
- updateWidth();
+ updateLayoutParams();
updateBubbleAccessibilityStates();
updateContentDescription();
mDismissedByDragBubbleView = null;
@@ -937,12 +937,6 @@
}
}
- private void updateWidth() {
- LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- lp.width = (int) (mIsBarExpanded ? expandedWidth() : collapsedWidth());
- setLayoutParams(lp);
- }
-
private void updateLayoutParams() {
LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
lp.height = (int) getBubbleBarExpandedHeight();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index cd0bca6..5c1a546 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -119,7 +119,8 @@
mBubbleDragController = bubbleControllers.bubbleDragController;
mTaskbarStashController = controllers.taskbarStashController;
mTaskbarInsetsController = controllers.taskbarInsetsController;
- mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
+ mBubbleBarViewAnimator = new BubbleBarViewAnimator(
+ mBarView, mBubbleStashController, mBubbleBarController::showExpandedView);
mTaskbarViewPropertiesProvider = taskbarViewPropertiesProvider;
onBubbleBarConfigurationChanged(/* animate= */ false);
mActivity.addOnDeviceProfileChangeListener(
@@ -308,6 +309,15 @@
return mBarView.getBubbleBarBounds();
}
+ /** Checks that bubble bar is visible and that the motion event is within bounds. */
+ public boolean isEventOverBubbleBar(MotionEvent event) {
+ if (!isBubbleBarVisible()) return false;
+ final Rect bounds = getBubbleBarBounds();
+ final int bubbleBarTopOnScreen = mBarView.getRestingTopPositionOnScreen();
+ final float x = event.getX();
+ return event.getRawY() >= bubbleBarTopOnScreen && x >= bounds.left && x <= bounds.right;
+ }
+
/** Whether a new bubble is animating. */
public boolean isAnimatingNewBubble() {
return mBubbleBarViewAnimator != null && mBubbleBarViewAnimator.isAnimating();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 656a266..54b883c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -77,6 +77,8 @@
private BubbleBarPinController mBubbleBarPinController;
private BubblePinController mBubblePinController;
+ private boolean mIsDragging;
+
public BubbleDragController(TaskbarActivityContext activity) {
mActivity = activity;
}
@@ -240,6 +242,16 @@
});
}
+ /** Whether there is an item being dragged or not. */
+ public boolean isDragging() {
+ return mIsDragging;
+ }
+
+ /** Sets whether something is being dragged or not. */
+ public void setIsDragging(boolean isDragging) {
+ mIsDragging = isDragging;
+ }
+
/**
* Bubble touch listener for handling a single bubble view or bubble bar view while dragging.
* The dragging starts after "shorter" long click (the long click duration might change):
@@ -436,6 +448,7 @@
private void startDragging(@NonNull View view) {
onDragStart();
+ BubbleDragController.this.setIsDragging(true);
mActivity.setTaskbarWindowFullscreen(true);
mAnimator = new BubbleDragAnimator(view);
mAnimator.animateFocused();
@@ -452,6 +465,7 @@
}
private void stopDragging(@NonNull View view, @NonNull MotionEvent event) {
+ BubbleDragController.this.setIsDragging(false);
Runnable onComplete = () -> {
mActivity.setTaskbarWindowFullscreen(false);
cleanUp(view);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 8158fe7..6bfe8f4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -298,15 +298,11 @@
}
// the bounds of the handle only include the visible part, so we check that the Y coordinate
- // is anywhere within the stashed taskbar height.
- int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight;
-
- return (int) ev.getRawY() >= top && containsX((int) ev.getRawX());
- }
-
- /** Checks if the given x coordinate is within the stashed handle bounds. */
- public boolean containsX(int x) {
- return x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right;
+ // is anywhere within the stashed height of bubble bar (same as taskbar stashed height).
+ final int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight;
+ final float x = ev.getRawX();
+ return ev.getRawY() >= top && x >= mStashedHandleBounds.left
+ && x <= mStashedHandleBounds.right;
}
/** Set a bubble bar location */
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index 2ed88d8..99c50f2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -36,6 +36,7 @@
constructor(
private val bubbleBarView: BubbleBarView,
private val bubbleStashController: BubbleStashController,
+ private val onExpanded: Runnable,
private val scheduler: Scheduler = HandlerScheduler(bubbleBarView)
) {
@@ -406,7 +407,7 @@
springBackAnimation.spring(DynamicAnimation.TRANSLATION_Y, ty)
springBackAnimation.addEndListener { _, _, _, _, _, _, _ ->
if (animatingBubble?.expand == true) {
- bubbleBarView.isExpanded = true
+ expandBubbleBar()
cancelHideAnimation()
} else {
moveToState(AnimatingBubble.State.IN)
@@ -417,7 +418,7 @@
ObjectAnimator.ofFloat(bubbleBarView, View.TRANSLATION_Y, ty - bubbleBarBounceDistanceInPx)
.withDuration(BUBBLE_BAR_BOUNCE_ANIMATION_DURATION_MS)
.withEndAction {
- if (animatingBubble?.expand == true) bubbleBarView.isExpanded = true
+ if (animatingBubble?.expand == true) expandBubbleBar()
springBackAnimation.start()
}
.start()
@@ -451,7 +452,7 @@
this.animatingBubble = animatingBubble.copy(expand = true)
// if we're fully in and waiting to hide, cancel the hide animation and clean up
if (animatingBubble.state == AnimatingBubble.State.IN) {
- bubbleBarView.isExpanded = true
+ expandBubbleBar()
cancelHideAnimation()
}
}
@@ -489,6 +490,11 @@
this.animatingBubble = animatingBubble.copy(state = state)
}
+ private fun expandBubbleBar() {
+ bubbleBarView.isExpanded = true
+ onExpanded.run()
+ }
+
/**
* Tracks the translation Y of the bubble bar during the animation. When the bubble bar expands
* as part of the animation, the expansion should start after the bubble bar reaches the peak
@@ -510,7 +516,7 @@
}
val expand = animatingBubble?.expand ?: false
if (reachedPeak && expand && !startedExpanding) {
- bubbleBarView.isExpanded = true
+ expandBubbleBar()
startedExpanding = true
}
previousTy = ty
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 110ca16..37e0034 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.Keyframe;
@@ -39,6 +40,7 @@
import android.os.Process;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.util.Property;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -71,12 +73,27 @@
*/
public class PredictedAppIcon extends DoubleShadowBubbleTextView {
+ private static final float RING_SCALE_START_VALUE = 0.75f;
private static final int RING_SHADOW_COLOR = 0x99000000;
private static final float RING_EFFECT_RATIO = 0.095f;
private static final long ICON_CHANGE_ANIM_DURATION = 360;
private static final long ICON_CHANGE_ANIM_STAGGER = 50;
+ private static final Property<PredictedAppIcon, Float> RING_SCALE_PROPERTY =
+ new Property<>(Float.TYPE, "ringScale") {
+ @Override
+ public Float get(PredictedAppIcon icon) {
+ return icon.mRingScale;
+ }
+
+ @Override
+ public void set(PredictedAppIcon icon, Float value) {
+ icon.mRingScale = value;
+ icon.invalidate();
+ }
+ };
+
boolean mIsDrawingDot = false;
private final DeviceProfile mDeviceProfile;
private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -96,6 +113,11 @@
private Animator mSlotMachineAnim;
private float mSlotMachineIconTranslationY;
+ // Used to animate the "ring" around predicted icons
+ private float mRingScale = 1f;
+ private boolean mForceHideRing = false;
+ private Animator mRingScaleAnim;
+
private static final FloatProperty<PredictedAppIcon> SLOT_MACHINE_TRANSLATION_Y =
new FloatProperty<PredictedAppIcon>("slotMachineTranslationY") {
@Override
@@ -356,17 +378,57 @@
}
}
+ @Override
+ public void setIconVisible(boolean visible) {
+ setForceHideRing(!visible);
+ super.setIconVisible(visible);
+ }
+
+ private void setForceHideRing(boolean forceHideRing) {
+ if (mForceHideRing == forceHideRing) {
+ return;
+ }
+ mForceHideRing = forceHideRing;
+
+ if (forceHideRing) {
+ invalidate();
+ } else {
+ animateRingScale(RING_SCALE_START_VALUE, 1);
+ }
+ }
+
+ private void cancelRingScaleAnim() {
+ if (mRingScaleAnim != null) {
+ mRingScaleAnim.cancel();
+ }
+ }
+
+ private void animateRingScale(float... ringScale) {
+ cancelRingScaleAnim();
+ mRingScaleAnim = ObjectAnimator.ofFloat(this, RING_SCALE_PROPERTY, ringScale);
+ mRingScaleAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRingScaleAnim = null;
+ }
+ });
+ mRingScaleAnim.start();
+ }
+
private void drawEffect(Canvas canvas) {
- // Don't draw ring effect if item is about to be dragged.
- if (mDrawForDrag) {
+ // Don't draw ring effect if item is about to be dragged or if the icon is not visible.
+ if (mDrawForDrag || !mIsIconVisible) {
return;
}
mIconRingPaint.setColor(RING_SHADOW_COLOR);
mIconRingPaint.setMaskFilter(mShadowFilter);
+ int count = canvas.save();
+ canvas.scale(mRingScale, mRingScale, canvas.getWidth() / 2f, canvas.getHeight() / 2f);
canvas.drawPath(mRingPath, mIconRingPaint);
mIconRingPaint.setColor(mPlateColor);
mIconRingPaint.setMaskFilter(null);
canvas.drawPath(mRingPath, mIconRingPaint);
+ canvas.restoreToCount(count);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index eafc5b6..202276e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -181,7 +181,7 @@
// - The task is snapped
mAllowGoingDown = i == mRecentsView.getCurrentPage()
&& DisplayController.getNavigationMode(mContainer).hasGestures
- && (!mRecentsView.showAsGrid() || mTaskBeingDragged.isFocusedTask())
+ && (!mRecentsView.showAsGrid() || mTaskBeingDragged.isLargeTile())
&& mRecentsView.isTaskInExpectedScrollPosition(i);
directionsToDetectScroll = mAllowGoingDown ? DIRECTION_BOTH : upDirection;
@@ -310,7 +310,7 @@
// Set mOverrideVelocity to control task dismiss velocity in onDragEnd
int velocityDimenId = R.dimen.default_task_dismiss_drag_velocity;
if (mRecentsView.showAsGrid()) {
- if (mTaskBeingDragged.isFocusedTask()) {
+ if (mTaskBeingDragged.isLargeTile()) {
velocityDimenId =
R.dimen.default_task_dismiss_drag_velocity_grid_focus_task;
} else {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 5392b70..3d94442 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -734,11 +734,18 @@
}
private void maybeUpdateRecentsAttachedState() {
- maybeUpdateRecentsAttachedState(true /* animate */);
+ maybeUpdateRecentsAttachedState(/* animate= */ true);
}
protected void maybeUpdateRecentsAttachedState(boolean animate) {
- maybeUpdateRecentsAttachedState(animate, false /* moveRunningTask */);
+ maybeUpdateRecentsAttachedState(animate, /* moveRunningTask= */ false);
+ }
+
+ protected void maybeUpdateRecentsAttachedState(boolean animate, boolean moveRunningTask) {
+ maybeUpdateRecentsAttachedState(
+ animate,
+ moveRunningTask,
+ mRecentsView != null && mRecentsView.shouldUpdateRunningTaskAlpha());
}
/**
@@ -749,8 +756,10 @@
* Note this method has no effect unless the navigation mode is NO_BUTTON.
* @param animate whether to animate when attaching RecentsView
* @param moveRunningTask whether to move running task to front when attaching
+ * @param updateRunningTaskAlpha Whether to update the running task's attached alpha
*/
- private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveRunningTask) {
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, boolean moveRunningTask, boolean updateRunningTaskAlpha) {
if ((!mDeviceState.isFullyGesturalNavMode() && !mGestureState.isTrackpadGesture())
|| mRecentsView == null) {
return;
@@ -781,7 +790,8 @@
// TaskView jumping to new position as we move the tasks.
mRecentsView.moveRunningTaskToFront();
}
- mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
+ mAnimationFactory.setRecentsAttachedToAppWindow(
+ recentsAttachedToAppWindow, animate, updateRunningTaskAlpha);
// Reapply window transform throughout the attach animation, as the animation affects how
// much the window is bound by overscroll (vs moving freely).
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 293944d..8703843 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -21,11 +21,13 @@
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_ATTACHED_ALPHA_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+import static com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.animation.Animator;
@@ -187,8 +189,10 @@
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
+ * @param updateRunningTaskAlpha Whether to update the running task's attached alpha
*/
- default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
+ default void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, boolean updateRunningTaskAlpha) { }
default boolean isRecentsAttachedToAppWindow() {
return false;
@@ -253,12 +257,14 @@
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
if (DisplayController.getNavigationMode(mActivity) == NavigationMode.NO_BUTTON) {
- setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
+ setRecentsAttachedToAppWindow(
+ mIsAttachedToWindow, false, recentsView.shouldUpdateRunningTaskAlpha());
}
}
@Override
- public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
+ public void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, boolean updateRunningTaskAlpha) {
if (mIsAttachedToWindow == attached && animate) {
return;
}
@@ -266,6 +272,10 @@
.cancelStateElementAnimation(INDEX_RECENTS_FADE_ANIM);
mActivity.getStateManager()
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+ if (updateRunningTaskAlpha) {
+ mActivity.getStateManager()
+ .cancelStateElementAnimation(INDEX_RECENTS_ATTACHED_ALPHA_ANIM);
+ }
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@@ -280,19 +290,28 @@
long animationDuration = animate ? RECENTS_ATTACH_DURATION : 0;
Animator fadeAnim = mActivity.getStateManager()
- .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+ .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1f : 0f);
fadeAnim.setInterpolator(attached ? INSTANT : ACCELERATE_2);
fadeAnim.setDuration(animationDuration);
animatorSet.play(fadeAnim);
float fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(
mActivity.getOverviewPanel());
- float toTranslation = attached ? 0 : 1;
-
+ float toTranslation = attached ? 0f : 1f;
Animator translationAnimator = mActivity.getStateManager().createStateElementAnimation(
INDEX_RECENTS_TRANSLATE_X_ANIM, fromTranslation, toTranslation);
translationAnimator.setDuration(animationDuration);
animatorSet.play(translationAnimator);
+
+ if (updateRunningTaskAlpha) {
+ float fromAlpha = RUNNING_TASK_ATTACH_ALPHA.get(mActivity.getOverviewPanel());
+ float toAlpha = attached ? 1f : 0f;
+ Animator runningTaskAttachAlphaAnimator = mActivity.getStateManager()
+ .createStateElementAnimation(
+ INDEX_RECENTS_ATTACHED_ALPHA_ANIM, fromAlpha, toAlpha);
+ runningTaskAttachAlphaAnimator.setDuration(animationDuration);
+ animatorSet.play(runningTaskAttachAlphaAnimator);
+ }
animatorSet.start();
}
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index fbc0d14..94f4920 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -24,8 +24,8 @@
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskContainer
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
/** A menu item, "Desktop", that allows the user to bring the current app into Desktop Windowing. */
class DesktopSystemShortcut(
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index f898e2f..0185737 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -40,6 +40,7 @@
int TYPE_STATUS_BAR = 1 << 13;
int TYPE_CURSOR_HOVER = 1 << 14;
int TYPE_NAV_HANDLE_LONG_PRESS = 1 << 15;
+ int TYPE_BUBBLE_BAR = 1 << 16;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -58,6 +59,7 @@
"TYPE_STATUS_BAR", // 13
"TYPE_CURSOR_HOVER", // 14
"TYPE_NAV_HANDLE_LONG_PRESS", // 15
+ "TYPE_BUBBLE_BAR", // 16
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 84f6b55..a03c0f8 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -37,6 +37,7 @@
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.window.CachedDisplayInfo;
+import com.android.systemui.shared.Flags;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -242,7 +243,8 @@
int rotation = display.rotation;
int touchHeight = mNavBarGesturalHeight;
OrientationRectF orientationRectF = new OrientationRectF(0, 0, size.x, size.y, rotation);
- if (mMode == NavigationMode.NO_BUTTON) {
+ if (mMode == NavigationMode.NO_BUTTON
+ || (mMode == NavigationMode.THREE_BUTTONS && Flags.threeButtonCornerSwipe())) {
orientationRectF.top = orientationRectF.bottom - touchHeight;
updateAssistantRegions(orientationRectF);
} else {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
deleted file mode 100644
index 8f533a3..0000000
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.PagedView.INVALID_PAGE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
-import com.android.internal.jank.Cuj;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logger.LauncherAtom;
-import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.taskbar.TaskbarUIController;
-import com.android.launcher3.util.RunnableList;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.RecentsViewContainer;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * Helper class to handle various atomic commands for switching between Overview.
- */
-public class OverviewCommandHelper {
- private static final String TAG = "OverviewCommandHelper";
-
- public static final int TYPE_SHOW = 1;
- public static final int TYPE_KEYBOARD_INPUT = 2;
- public static final int TYPE_HIDE = 3;
- public static final int TYPE_TOGGLE = 4;
- public static final int TYPE_HOME = 5;
-
- /**
- * Use case for needing a queue is double tapping recents button in 3 button nav.
- * Size of 2 should be enough. We'll toss in one more because we're kind hearted.
- */
- private final static int MAX_QUEUE_SIZE = 3;
-
- private static final String TRANSITION_NAME = "Transition:toOverview";
-
- private final TouchInteractionService mService;
- private final OverviewComponentObserver mOverviewComponentObserver;
- private final TaskAnimationManager mTaskAnimationManager;
- private final ArrayList<CommandInfo> mPendingCommands = new ArrayList<>();
-
- /**
- * Index of the TaskView that should be focused when launching Overview. Persisted so that we
- * do not lose the focus across multiple calls of
- * {@link OverviewCommandHelper#executeCommand(CommandInfo)} for the same command
- */
- private int mKeyboardTaskFocusIndex = -1;
-
- /**
- * Whether we should incoming toggle commands while a previous toggle command is still ongoing.
- * This serves as a rate-limiter to prevent overlapping animations that can clobber each other
- * and prevent clean-up callbacks from running. This thus prevents a recurring set of bugs with
- * janky recents animations and unresponsive home and overview buttons.
- */
- private boolean mWaitForToggleCommandComplete = false;
-
- public OverviewCommandHelper(TouchInteractionService service,
- OverviewComponentObserver observer,
- TaskAnimationManager taskAnimationManager) {
- mService = service;
- mOverviewComponentObserver = observer;
- mTaskAnimationManager = taskAnimationManager;
- }
-
- /**
- * Called when the command finishes execution.
- */
- private void scheduleNextTask(CommandInfo command) {
- if (mPendingCommands.isEmpty()) {
- Log.d(TAG, "no pending commands to schedule");
- return;
- }
- if (mPendingCommands.get(0) != command) {
- Log.d(TAG, "next task not scheduled."
- + " mPendingCommands[0] type is " + mPendingCommands.get(0)
- + " - command type is: " + command);
- return;
- }
- Log.d(TAG, "scheduleNextTask called: " + command);
- mPendingCommands.remove(0);
- executeNext();
- }
-
- /**
- * Executes the next command from the queue. If the command finishes immediately (returns true),
- * it continues to execute the next command, until the queue is empty of a command defer's its
- * completion (returns false).
- */
- @UiThread
- private void executeNext() {
- if (mPendingCommands.isEmpty()) {
- Log.d(TAG, "executeNext - mPendingCommands is empty");
- return;
- }
- CommandInfo cmd = mPendingCommands.get(0);
-
- boolean result = executeCommand(cmd);
- Log.d(TAG, "executeNext cmd type: " + cmd + ", result: " + result);
- if (result) {
- scheduleNextTask(cmd);
- }
- }
-
- @UiThread
- private void addCommand(CommandInfo cmd) {
- boolean wasEmpty = mPendingCommands.isEmpty();
- mPendingCommands.add(cmd);
- if (wasEmpty) {
- executeNext();
- }
- }
-
- /**
- * Adds a command to be executed next, after all pending tasks are completed.
- * Max commands that can be queued is {@link #MAX_QUEUE_SIZE}.
- * Requests after reaching that limit will be silently dropped.
- */
- @BinderThread
- public void addCommand(int type) {
- if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
- Log.d(TAG, "the pending command queue is full (" + mPendingCommands.size() + "). "
- + "command not added: " + type);
- return;
- }
- Log.d(TAG, "adding command type: " + type);
- CommandInfo cmd = new CommandInfo(type);
- MAIN_EXECUTOR.execute(() -> addCommand(cmd));
- }
-
- @UiThread
- public void clearPendingCommands() {
- Log.d(TAG, "clearing pending commands - size: " + mPendingCommands.size());
- mPendingCommands.clear();
- }
-
- @UiThread
- public boolean canStartHomeSafely() {
- return mPendingCommands.isEmpty() || mPendingCommands.get(0).type == TYPE_HOME;
- }
-
- @Nullable
- private TaskView getNextTask(RecentsView view) {
- final TaskView runningTaskView = view.getRunningTaskView();
-
- if (runningTaskView == null) {
- return view.getTaskViewAt(0);
- } else {
- final TaskView nextTask = view.getNextTaskView();
- return nextTask != null ? nextTask : runningTaskView;
- }
- }
-
- private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
- RunnableList callbackList = null;
- if (taskView != null) {
- mWaitForToggleCommandComplete = true;
- taskView.setEndQuickSwitchCuj(true);
- callbackList = taskView.launchTasks();
- }
-
- if (callbackList != null) {
- callbackList.add(() -> {
- Log.d(TAG, "launching task callback: " + cmd);
- scheduleNextTask(cmd);
- mWaitForToggleCommandComplete = false;
- });
- Log.d(TAG, "launching task - waiting for callback: " + cmd);
- return false;
- } else {
- recents.startHome();
- mWaitForToggleCommandComplete = false;
- return true;
- }
- }
-
- /**
- * Executes the task and returns true if next task can be executed. If false, then the next
- * task is deferred until {@link #scheduleNextTask} is called
- */
- private <T extends StatefulActivity<?> & RecentsViewContainer> boolean executeCommand(
- CommandInfo cmd) {
- if (mWaitForToggleCommandComplete && cmd.type == TYPE_TOGGLE) {
- Log.d(TAG, "executeCommand: " + cmd
- + " - waiting for toggle command complete");
- return true;
- }
- BaseActivityInterface<?, T> activityInterface =
- mOverviewComponentObserver.getActivityInterface();
-
- RecentsView<?, ?> visibleRecentsView = activityInterface.getVisibleRecentsView();
- RecentsView<?, ?> createdRecentsView;
-
- Log.d(TAG, "executeCommand: " + cmd
- + " - visibleRecentsView: " + visibleRecentsView);
- if (visibleRecentsView == null) {
- T activity = activityInterface.getCreatedContainer();
- createdRecentsView = activity == null ? null : activity.getOverviewPanel();
- DeviceProfile dp = activity == null ? null : activity.getDeviceProfile();
- TaskbarUIController uiController = activityInterface.getTaskbarController();
- boolean allowQuickSwitch = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
- && uiController != null
- && dp != null
- && (dp.isTablet || dp.isTwoPanels);
-
- switch (cmd.type) {
- case TYPE_HIDE:
- if (!allowQuickSwitch) {
- return true;
- }
- mKeyboardTaskFocusIndex = uiController.launchFocusedTask();
- if (mKeyboardTaskFocusIndex == -1) {
- return true;
- }
- break;
- case TYPE_KEYBOARD_INPUT:
- if (allowQuickSwitch) {
- uiController.openQuickSwitchView();
- return true;
- } else {
- mKeyboardTaskFocusIndex = 0;
- break;
- }
- case TYPE_HOME:
- ActiveGestureLog.INSTANCE.addLog(
- "OverviewCommandHelper.executeCommand(TYPE_HOME)");
- // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
- // we should still call it on main thread because launcher is waiting for
- // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
- // could potentially delay resuming launcher. See b/348668521 for more details.
- mService.startActivity(mOverviewComponentObserver.getHomeIntent());
- return true;
- case TYPE_SHOW:
- // When Recents is not currently visible, the command's type is TYPE_SHOW
- // when overview is triggered via the keyboard overview button or Action+Tab
- // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
- // nav is TYPE_TOGGLE.
- mKeyboardTaskFocusIndex = 0;
- break;
- default:
- // continue below to handle displaying Recents.
- }
- } else {
- createdRecentsView = visibleRecentsView;
- switch (cmd.type) {
- case TYPE_SHOW:
- // already visible
- return true;
- case TYPE_KEYBOARD_INPUT: {
- if (visibleRecentsView.isHandlingTouch()) {
- return true;
- }
- }
- case TYPE_HIDE: {
- if (visibleRecentsView.isHandlingTouch()) {
- return true;
- }
- mKeyboardTaskFocusIndex = INVALID_PAGE;
- int currentPage = visibleRecentsView.getNextPage();
- TaskView tv = (currentPage >= 0
- && currentPage < visibleRecentsView.getTaskViewCount())
- ? (TaskView) visibleRecentsView.getPageAt(currentPage)
- : null;
- return launchTask(visibleRecentsView, tv, cmd);
- }
- case TYPE_TOGGLE:
- return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), cmd);
- case TYPE_HOME:
- visibleRecentsView.startHome();
- return true;
- }
- }
-
- if (createdRecentsView != null) {
- createdRecentsView.setKeyboardTaskFocusIndex(mKeyboardTaskFocusIndex);
- }
- // Handle recents view focus when launching from home
- Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- updateRecentsViewFocus(cmd);
- logShowOverviewFrom(cmd.type);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d(TAG, "switching to Overview state - onAnimationEnd: " + cmd);
- super.onAnimationEnd(animation);
- onRecentsViewFocusUpdated(cmd);
- scheduleNextTask(cmd);
- }
- };
- if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
- Log.d(TAG, "switching to Overview state - waiting: " + cmd);
- // If successfully switched, wait until animation finishes
- return false;
- }
-
- final T activity = activityInterface.getCreatedContainer();
- if (activity != null) {
- InteractionJankMonitorWrapper.begin(
- activity.getRootView(),
- Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
- }
-
- GestureState gestureState = mService.createGestureState(GestureState.DEFAULT_STATE,
- GestureState.TrackpadGestureType.NONE);
- gestureState.setHandlingAtomicEvent(true);
- AbsSwipeUpHandler interactionHandler = mService.getSwipeUpHandlerFactory()
- .newHandler(gestureState, cmd.createTime);
- interactionHandler.setGestureEndCallback(
- () -> onTransitionComplete(cmd, interactionHandler));
- interactionHandler.initWhenReady("OverviewCommandHelper: cmd.type=" + cmd.type);
-
- RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- updateRecentsViewFocus(cmd);
- logShowOverviewFrom(cmd.type);
- activityInterface.runOnInitBackgroundStateUI(() ->
- interactionHandler.onGestureEnded(0, new PointF()));
- cmd.removeListener(this);
- }
-
- @Override
- public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
- interactionHandler.onGestureCancelled();
- cmd.removeListener(this);
-
- T createdActivity = activityInterface.getCreatedContainer();
- if (createdActivity == null) {
- return;
- }
- if (createdRecentsView != null) {
- createdRecentsView.onRecentsAnimationComplete();
- }
- }
- };
-
- if (visibleRecentsView != null) {
- visibleRecentsView.moveRunningTaskToFront();
- }
- if (mTaskAnimationManager.isRecentsAnimationRunning()) {
- cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState);
- cmd.mActiveCallbacks.addListener(interactionHandler);
- mTaskAnimationManager.notifyRecentsAnimationState(interactionHandler);
- interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/);
-
- cmd.mActiveCallbacks.addListener(recentAnimListener);
- mTaskAnimationManager.notifyRecentsAnimationState(recentAnimListener);
- } else {
- Intent intent = new Intent(interactionHandler.getLaunchIntent());
- intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
- cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
- gestureState, intent, interactionHandler);
- interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
- cmd.mActiveCallbacks.addListener(recentAnimListener);
- }
- Trace.beginAsyncSection(TRANSITION_NAME, 0);
- Log.d(TAG, "switching via recents animation - onGestureStarted: " + cmd);
- return false;
- }
-
- private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
- Log.d(TAG, "switching via recents animation - onTransitionComplete: " + cmd);
- cmd.removeListener(handler);
- Trace.endAsyncSection(TRANSITION_NAME, 0);
- onRecentsViewFocusUpdated(cmd);
- scheduleNextTask(cmd);
- }
-
- private void updateRecentsViewFocus(CommandInfo cmd) {
- RecentsView recentsView =
- mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
- if (recentsView == null || (cmd.type != TYPE_KEYBOARD_INPUT && cmd.type != TYPE_HIDE
- && cmd.type != TYPE_SHOW)) {
- return;
- }
- // When the overview is launched via alt tab (cmd type is TYPE_KEYBOARD_INPUT),
- // the touch mode somehow is not change to false by the Android framework.
- // The subsequent tab to go through tasks in overview can only be dispatched to
- // focuses views, while focus can only be requested in
- // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
- // here we launch overview with live tile.
- recentsView.getViewRootImpl().touchModeChanged(false);
- // Ensure that recents view has focus so that it receives the followup key inputs
- if (requestFocus(recentsView.getTaskViewAt(mKeyboardTaskFocusIndex))) {
- return;
- }
- if (requestFocus(recentsView.getNextTaskView())) {
- return;
- }
- if (requestFocus(recentsView.getTaskViewAt(0))) {
- return;
- }
- requestFocus(recentsView);
- }
-
- private void onRecentsViewFocusUpdated(CommandInfo cmd) {
- RecentsView recentsView =
- mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
- if (recentsView == null
- || cmd.type != TYPE_HIDE
- || mKeyboardTaskFocusIndex == INVALID_PAGE) {
- return;
- }
- recentsView.setKeyboardTaskFocusIndex(INVALID_PAGE);
- recentsView.setCurrentPage(mKeyboardTaskFocusIndex);
- mKeyboardTaskFocusIndex = INVALID_PAGE;
- }
-
- private boolean requestFocus(@Nullable View taskView) {
- if (taskView == null) {
- return false;
- }
- taskView.post(() -> {
- taskView.requestFocus();
- taskView.requestAccessibilityFocus();
- });
- return true;
- }
-
- private <T extends StatefulActivity<?> & RecentsViewContainer>
- void logShowOverviewFrom(int cmdType) {
- BaseActivityInterface<?, T> activityInterface =
- mOverviewComponentObserver.getActivityInterface();
- var container = activityInterface.getCreatedContainer();
- if (container != null) {
- StatsLogManager.LauncherEvent event;
- switch (cmdType) {
- case TYPE_SHOW -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
- case TYPE_HIDE ->
- event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
- case TYPE_TOGGLE -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
- default -> {
- return;
- }
- }
-
- StatsLogManager.newInstance(container.asContext())
- .logger()
- .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
- .setTaskSwitcherContainer(
- LauncherAtom.TaskSwitcherContainer.getDefaultInstance())
- .build())
- .log(event);
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println("OverviewCommandHelper:");
- pw.println(" mPendingCommands=" + mPendingCommands.size());
- if (!mPendingCommands.isEmpty()) {
- pw.println(" pendingCommandType=" + mPendingCommands.get(0).type);
- }
- pw.println(" mKeyboardTaskFocusIndex=" + mKeyboardTaskFocusIndex);
- pw.println(" mWaitForToggleCommandComplete=" + mWaitForToggleCommandComplete);
- }
-
- private static class CommandInfo {
- public final long createTime = SystemClock.elapsedRealtime();
- public final int type;
- RecentsAnimationCallbacks mActiveCallbacks;
-
- CommandInfo(int type) {
- this.type = type;
- }
-
- void removeListener(RecentsAnimationListener listener) {
- if (mActiveCallbacks != null) {
- mActiveCallbacks.removeListener(listener);
- }
- }
-
- @NonNull
- @Override
- public String toString() {
- return "CommandInfo("
- + "type=" + type + ", "
- + "createTime=" + createTime + ", "
- + "mActiveCallbacks=" + mActiveCallbacks
- + ")";
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
new file mode 100644
index 0000000..f6b9e4e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.content.Intent
+import android.graphics.PointF
+import android.os.SystemClock
+import android.os.Trace
+import android.util.Log
+import android.view.View
+import androidx.annotation.BinderThread
+import androidx.annotation.UiThread
+import com.android.internal.jank.Cuj
+import com.android.launcher3.PagedView
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.logger.LauncherAtom
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.*
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.RunnableList
+import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskView
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper
+import java.io.PrintWriter
+
+/** Helper class to handle various atomic commands for switching between Overview. */
+class OverviewCommandHelper(
+ private val touchInteractionService: TouchInteractionService,
+ private val overviewComponentObserver: OverviewComponentObserver,
+ private val taskAnimationManager: TaskAnimationManager
+) {
+ private val pendingCommands = mutableListOf<CommandInfo>()
+
+ /**
+ * Index of the TaskView that should be focused when launching Overview. Persisted so that we do
+ * not lose the focus across multiple calls of [OverviewCommandHelper.executeCommand] for the
+ * same command
+ */
+ private var keyboardTaskFocusIndex = -1
+
+ /**
+ * Whether we should incoming toggle commands while a previous toggle command is still ongoing.
+ * This serves as a rate-limiter to prevent overlapping animations that can clobber each other
+ * and prevent clean-up callbacks from running. This thus prevents a recurring set of bugs with
+ * janky recents animations and unresponsive home and overview buttons.
+ */
+ private var waitForToggleCommandComplete = false
+
+ /** Called when the command finishes execution. */
+ private fun scheduleNextTask(command: CommandInfo) {
+ if (pendingCommands.isEmpty()) {
+ Log.d(TAG, "no pending commands to schedule")
+ return
+ }
+ if (pendingCommands.first() !== command) {
+ Log.d(
+ TAG,
+ "next task not scheduled. First pending command type " +
+ "is ${pendingCommands.first()} - command type is: $command"
+ )
+ return
+ }
+ Log.d(TAG, "scheduleNextTask called: $command")
+ pendingCommands.removeFirst()
+ executeNext()
+ }
+
+ /**
+ * Executes the next command from the queue. If the command finishes immediately (returns true),
+ * it continues to execute the next command, until the queue is empty of a command defer's its
+ * completion (returns false).
+ */
+ @UiThread
+ private fun executeNext() {
+ if (pendingCommands.isEmpty()) {
+ Log.d(TAG, "executeNext - pendingCommands is empty")
+ return
+ }
+ val command = pendingCommands.first()
+ val result = executeCommand(command)
+ Log.d(TAG, "executeNext command type: $command, result: $result")
+ if (result) {
+ scheduleNextTask(command)
+ }
+ }
+
+ @UiThread
+ private fun addCommand(command: CommandInfo) {
+ val wasEmpty = pendingCommands.isEmpty()
+ pendingCommands.add(command)
+ if (wasEmpty) {
+ executeNext()
+ }
+ }
+
+ /**
+ * Adds a command to be executed next, after all pending tasks are completed. Max commands that
+ * can be queued is [.MAX_QUEUE_SIZE]. Requests after reaching that limit will be silently
+ * dropped.
+ */
+ @BinderThread
+ fun addCommand(type: Int) {
+ if (pendingCommands.size >= MAX_QUEUE_SIZE) {
+ Log.d(
+ TAG,
+ "the pending command queue is full (${pendingCommands.size}). command not added: $type"
+ )
+ return
+ }
+ Log.d(TAG, "adding command type: $type")
+ val command = CommandInfo(type)
+ Executors.MAIN_EXECUTOR.execute { addCommand(command) }
+ }
+
+ @UiThread
+ fun clearPendingCommands() {
+ Log.d(TAG, "clearing pending commands - size: ${pendingCommands.size}")
+ pendingCommands.clear()
+ }
+
+ @UiThread
+ fun canStartHomeSafely(): Boolean =
+ pendingCommands.isEmpty() || pendingCommands.first().type == TYPE_HOME
+
+ private fun getNextTask(view: RecentsView<*, *>): TaskView? {
+ val runningTaskView = view.runningTaskView
+
+ return if (runningTaskView == null) {
+ view.getTaskViewAt(0)
+ } else {
+ val nextTask = view.nextTaskView
+ nextTask ?: runningTaskView
+ }
+ }
+
+ private fun launchTask(
+ recents: RecentsView<*, *>,
+ taskView: TaskView?,
+ command: CommandInfo
+ ): Boolean {
+ var callbackList: RunnableList? = null
+ if (taskView != null) {
+ waitForToggleCommandComplete = true
+ taskView.isEndQuickSwitchCuj = true
+ callbackList = taskView.launchTasks()
+ }
+
+ if (callbackList != null) {
+ callbackList.add {
+ Log.d(TAG, "launching task callback: $command")
+ scheduleNextTask(command)
+ waitForToggleCommandComplete = false
+ }
+ Log.d(TAG, "launching task - waiting for callback: $command")
+ return false
+ } else {
+ recents.startHome()
+ waitForToggleCommandComplete = false
+ return true
+ }
+ }
+
+ /**
+ * Executes the task and returns true if next task can be executed. If false, then the next task
+ * is deferred until [.scheduleNextTask] is called
+ */
+ private fun executeCommand(command: CommandInfo): Boolean {
+ if (waitForToggleCommandComplete && command.type == TYPE_TOGGLE) {
+ Log.d(TAG, "executeCommand: $command - waiting for toggle command complete")
+ return true
+ }
+ val activityInterface: BaseActivityInterface<*, *> =
+ overviewComponentObserver.activityInterface
+
+ val visibleRecentsView: RecentsView<*, *>? =
+ activityInterface.getVisibleRecentsView<RecentsView<*, *>>()
+ val createdRecentsView: RecentsView<*, *>?
+
+ Log.d(TAG, "executeCommand: $command - visibleRecentsView: $visibleRecentsView")
+ if (visibleRecentsView == null) {
+ val activity = activityInterface.getCreatedContainer() as? RecentsViewContainer
+ createdRecentsView = activity?.getOverviewPanel()
+ val deviceProfile = activity?.getDeviceProfile()
+ val uiController = activityInterface.getTaskbarController()
+ val allowQuickSwitch =
+ FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get() &&
+ uiController != null &&
+ deviceProfile != null &&
+ (deviceProfile.isTablet || deviceProfile.isTwoPanels)
+
+ when (command.type) {
+ TYPE_HIDE -> {
+ if (!allowQuickSwitch) return true
+ keyboardTaskFocusIndex = uiController!!.launchFocusedTask()
+ if (keyboardTaskFocusIndex == -1) return true
+ }
+ TYPE_KEYBOARD_INPUT ->
+ if (allowQuickSwitch) {
+ uiController!!.openQuickSwitchView()
+ return true
+ } else {
+ keyboardTaskFocusIndex = 0
+ }
+ TYPE_HOME -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ "OverviewCommandHelper.executeCommand(TYPE_HOME)"
+ )
+ // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
+ // we should still call it on main thread because launcher is waiting for
+ // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
+ // could potentially delay resuming launcher. See b/348668521 for more details.
+ touchInteractionService.startActivity(overviewComponentObserver.homeIntent)
+ return true
+ }
+ TYPE_SHOW ->
+ // When Recents is not currently visible, the command's type is
+ // TYPE_SHOW
+ // when overview is triggered via the keyboard overview button or Action+Tab
+ // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
+ // nav is TYPE_TOGGLE.
+ keyboardTaskFocusIndex = 0
+ else -> {}
+ }
+ } else {
+ createdRecentsView = visibleRecentsView
+ when (command.type) {
+ TYPE_SHOW -> return true // already visible
+ TYPE_KEYBOARD_INPUT,
+ TYPE_HIDE -> {
+ if (visibleRecentsView.isHandlingTouch) return true
+
+ keyboardTaskFocusIndex = PagedView.INVALID_PAGE
+ val currentPage = visibleRecentsView.nextPage
+ val taskView = visibleRecentsView.getTaskViewAt(currentPage)
+ return launchTask(visibleRecentsView, taskView, command)
+ }
+ TYPE_TOGGLE ->
+ return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), command)
+ TYPE_HOME -> {
+ visibleRecentsView.startHome()
+ return true
+ }
+ }
+ }
+
+ createdRecentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex)
+ // Handle recents view focus when launching from home
+ val animatorListener: Animator.AnimatorListener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator) {
+ super.onAnimationStart(animation)
+ updateRecentsViewFocus(command)
+ logShowOverviewFrom(command.type)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ Log.d(TAG, "switching to Overview state - onAnimationEnd: $command")
+ super.onAnimationEnd(animation)
+ onRecentsViewFocusUpdated(command)
+ scheduleNextTask(command)
+ }
+ }
+ if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
+ Log.d(TAG, "switching to Overview state - waiting: $command")
+ // If successfully switched, wait until animation finishes
+ return false
+ }
+
+ val activity = activityInterface.getCreatedContainer()
+ if (activity != null) {
+ InteractionJankMonitorWrapper.begin(activity.rootView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
+ }
+
+ val gestureState =
+ touchInteractionService.createGestureState(
+ GestureState.DEFAULT_STATE,
+ GestureState.TrackpadGestureType.NONE
+ )
+ gestureState.isHandlingAtomicEvent = true
+ val interactionHandler =
+ touchInteractionService.swipeUpHandlerFactory.newHandler(
+ gestureState,
+ command.createTime
+ )
+ interactionHandler.setGestureEndCallback {
+ onTransitionComplete(command, interactionHandler)
+ }
+ interactionHandler.initWhenReady("OverviewCommandHelper: command.type=${command.type}")
+
+ val recentAnimListener: RecentsAnimationCallbacks.RecentsAnimationListener =
+ object : RecentsAnimationCallbacks.RecentsAnimationListener {
+ override fun onRecentsAnimationStart(
+ controller: RecentsAnimationController,
+ targets: RecentsAnimationTargets
+ ) {
+ updateRecentsViewFocus(command)
+ logShowOverviewFrom(command.type)
+ activityInterface.runOnInitBackgroundStateUI {
+ interactionHandler.onGestureEnded(0f, PointF())
+ }
+ command.removeListener(this)
+ }
+
+ override fun onRecentsAnimationCanceled(
+ thumbnailDatas: HashMap<Int, ThumbnailData>
+ ) {
+ interactionHandler.onGestureCancelled()
+ command.removeListener(this)
+
+ activityInterface.getCreatedContainer() ?: return
+ createdRecentsView?.onRecentsAnimationComplete()
+ }
+ }
+
+ // TODO(b/361768912): Dead code. Remove or update after this bug is fixed.
+ // if (visibleRecentsView != null) {
+ // visibleRecentsView.moveRunningTaskToFront();
+ // }
+
+ if (taskAnimationManager.isRecentsAnimationRunning) {
+ command.setAnimationCallbacks(
+ taskAnimationManager.continueRecentsAnimation(gestureState)
+ )
+ command.addListener(interactionHandler)
+ taskAnimationManager.notifyRecentsAnimationState(interactionHandler)
+ interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/)
+
+ command.addListener(recentAnimListener)
+ taskAnimationManager.notifyRecentsAnimationState(recentAnimListener)
+ } else {
+ val intent =
+ Intent(interactionHandler.launchIntent)
+ .putExtra(ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID, gestureState.gestureId)
+ command.setAnimationCallbacks(
+ taskAnimationManager.startRecentsAnimation(gestureState, intent, interactionHandler)
+ )
+ interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/)
+ command.addListener(recentAnimListener)
+ }
+ Trace.beginAsyncSection(TRANSITION_NAME, 0)
+ Log.d(TAG, "switching via recents animation - onGestureStarted: $command")
+ return false
+ }
+
+ private fun onTransitionComplete(command: CommandInfo, handler: AbsSwipeUpHandler<*, *, *>) {
+ Log.d(TAG, "switching via recents animation - onTransitionComplete: $command")
+ command.removeListener(handler)
+ Trace.endAsyncSection(TRANSITION_NAME, 0)
+ onRecentsViewFocusUpdated(command)
+ scheduleNextTask(command)
+ }
+
+ private fun updateRecentsViewFocus(command: CommandInfo) {
+ val recentsView: RecentsView<*, *> =
+ overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return
+ if (
+ command.type != TYPE_KEYBOARD_INPUT &&
+ command.type != TYPE_HIDE &&
+ command.type != TYPE_SHOW
+ ) {
+ return
+ }
+
+ // When the overview is launched via alt tab (command type is TYPE_KEYBOARD_INPUT),
+ // the touch mode somehow is not change to false by the Android framework.
+ // The subsequent tab to go through tasks in overview can only be dispatched to
+ // focuses views, while focus can only be requested in
+ // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
+ // here we launch overview with live tile.
+ recentsView.viewRootImpl.touchModeChanged(false)
+ // Ensure that recents view has focus so that it receives the followup key inputs
+ if (requestFocus(recentsView.getTaskViewAt(keyboardTaskFocusIndex))) return
+ if (requestFocus(recentsView.nextTaskView)) return
+ if (requestFocus(recentsView.getTaskViewAt(0))) return
+ requestFocus(recentsView)
+ }
+
+ private fun onRecentsViewFocusUpdated(command: CommandInfo) {
+ val recentsView: RecentsView<*, *> =
+ overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return
+ if (command.type != TYPE_HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
+ return
+ }
+ recentsView.setKeyboardTaskFocusIndex(PagedView.INVALID_PAGE)
+ recentsView.currentPage = keyboardTaskFocusIndex
+ keyboardTaskFocusIndex = PagedView.INVALID_PAGE
+ }
+
+ private fun requestFocus(taskView: View?): Boolean {
+ if (taskView == null) return false
+ taskView.post {
+ taskView.requestFocus()
+ taskView.requestAccessibilityFocus()
+ }
+ return true
+ }
+
+ private fun logShowOverviewFrom(commandType: Int) {
+ val activityInterface: BaseActivityInterface<*, *> =
+ overviewComponentObserver.activityInterface
+ val container = activityInterface.getCreatedContainer() as? RecentsViewContainer ?: return
+ val event =
+ when (commandType) {
+ TYPE_SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
+ TYPE_HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
+ TYPE_TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
+ else -> return
+ }
+ StatsLogManager.newInstance(container.asContext())
+ .logger()
+ .withContainerInfo(
+ LauncherAtom.ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(
+ LauncherAtom.TaskSwitcherContainer.getDefaultInstance()
+ )
+ .build()
+ )
+ .log(event)
+ }
+
+ fun dump(pw: PrintWriter) {
+ pw.println("OverviewCommandHelper:")
+ pw.println(" pendingCommands=${pendingCommands.size}")
+ if (pendingCommands.isNotEmpty()) {
+ pw.println(" pendingCommandType=${pendingCommands.first().type}")
+ }
+ pw.println(" mKeyboardTaskFocusIndex=$keyboardTaskFocusIndex")
+ pw.println(" mWaitForToggleCommandComplete=$waitForToggleCommandComplete")
+ }
+
+ private data class CommandInfo(
+ val type: Int,
+ val createTime: Long = SystemClock.elapsedRealtime(),
+ private var animationCallbacks: RecentsAnimationCallbacks? = null
+ ) {
+ fun setAnimationCallbacks(recentsAnimationCallbacks: RecentsAnimationCallbacks) {
+ this.animationCallbacks = recentsAnimationCallbacks
+ }
+
+ fun addListener(listener: RecentsAnimationCallbacks.RecentsAnimationListener) {
+ animationCallbacks?.addListener(listener)
+ }
+
+ fun removeListener(listener: RecentsAnimationCallbacks.RecentsAnimationListener?) {
+ animationCallbacks?.removeListener(listener)
+ }
+ }
+
+ companion object {
+ private const val TAG = "OverviewCommandHelper"
+
+ const val TYPE_SHOW: Int = 1
+ const val TYPE_KEYBOARD_INPUT: Int = 2
+ const val TYPE_HIDE: Int = 3
+ const val TYPE_TOGGLE: Int = 4
+ const val TYPE_HOME: Int = 5
+
+ /**
+ * Use case for needing a queue is double tapping recents button in 3 button nav. Size of 2
+ * should be enough. We'll toss in one more because we're kind hearted.
+ */
+ private const val MAX_QUEUE_SIZE = 3
+
+ private const val TRANSITION_NAME = "Transition:toOverview"
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index b290f83..49b6f57 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,16 +1,12 @@
package com.android.quickstep;
import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
-import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.PointF;
-import android.graphics.Rect;
import android.os.Bundle;
-import android.util.Log;
import androidx.annotation.Nullable;
@@ -18,7 +14,6 @@
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.DisplayController;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.TISBindHelper;
@@ -82,29 +77,10 @@
}
case TestProtocol.REQUEST_GET_OVERVIEW_TASK_SIZE: {
- Log.d(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH, "== REQUEST_GET_OVERVIEW_TASK_SIZE ==");
- Rect gridSize = new Rect();
- LauncherActivityInterface.INSTANCE.calculateGridSize(mDeviceProfile, mContext,
- gridSize);
- Log.d(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH, "gridSize: " + gridSize);
- PointF taskDimension = new PointF();
- LauncherActivityInterface.getTaskDimension(mContext, mDeviceProfile, taskDimension);
- Log.d(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH,
- "taskbarHeight: " + mDeviceProfile.taskbarHeight);
- Log.d(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH, "taskDimension: " + taskDimension);
- Rect taskSize = new Rect();
- LauncherActivityInterface.INSTANCE.calculateTaskSize(mContext, mDeviceProfile,
- taskSize, RecentsPagedOrientationHandler.PORTRAIT);
- Log.d(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH, "calculateTaskSize: " + taskSize);
return getUIProperty(Bundle::putParcelable,
- recentsViewContainer -> {
- Rect lastComputedTaskSize =
- recentsViewContainer.<RecentsView<?, ?>>getOverviewPanel()
- .getLastComputedTaskSize();
- Log.d(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH,
- "lastComputedTaskSize: " + lastComputedTaskSize);
- return lastComputedTaskSize;
- },
+ recentsViewContainer ->
+ recentsViewContainer.<RecentsView<?, ?>>getOverviewPanel()
+ .getLastComputedTaskSize(),
this::getRecentsViewContainer);
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index e66da52..05bef35 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,7 +20,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -40,8 +40,8 @@
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index f902284..5131774 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -74,6 +74,7 @@
import com.android.quickstep.util.GestureExclusionManager;
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener;
import com.android.quickstep.util.NavBarPosition;
+import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -548,6 +549,13 @@
}
/**
+ * @return whether the Assistant gesture can be used in 3 button navigation mode.
+ */
+ public boolean supportsAssistantGestureInButtonNav() {
+ return Flags.threeButtonCornerSwipe();
+ }
+
+ /**
* @param ev An ACTION_DOWN motion event
* @return whether the given motion event can trigger the assistant over the current task.
*/
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index db03dac..a01ceb2 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.recents.data.RecentTasksDataSource;
+import com.android.quickstep.recents.data.TaskVisualsChangeNotifier;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -65,7 +66,8 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
- TaskStackChangeListener, TaskVisualsChangeListener, SafeCloseable {
+ TaskStackChangeListener, TaskVisualsChangeListener, TaskVisualsChangeNotifier,
+ SafeCloseable {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -287,6 +289,7 @@
/**
* Adds a listener for visuals changes
*/
+ @Override
public void addThumbnailChangeListener(TaskVisualsChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
@@ -294,6 +297,7 @@
/**
* Removes a previously added listener
*/
+ @Override
public void removeThumbnailChangeListener(TaskVisualsChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 3dec381..1be60de 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,7 +17,7 @@
package com.android.quickstep;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
import android.app.WindowConfiguration;
import android.content.Context;
@@ -33,7 +33,7 @@
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
-import com.android.wm.shell.util.SplitBounds;
+import com.android.wm.shell.shared.split.SplitBounds;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 6f1ab7d..80c07196 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -41,6 +41,7 @@
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SafeCloseable;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.Flags;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -157,7 +158,7 @@
// Register for navigation mode changes
mDisplayController.addChangeListener(this);
DisplayController.Info info = mDisplayController.getInfo();
- onDisplayInfoChangedInternal(info, CHANGE_ALL, info.getNavigationMode().hasGestures);
+ onDisplayInfoChangedInternal(info, CHANGE_ALL, hasGestures(info.getNavigationMode()));
runOnDestroy(() -> mDisplayController.removeChangeListener(this));
mOrientationListener = new OrientationEventListener(mContext) {
@@ -229,7 +230,7 @@
* Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
*/
public void updateGestureTouchRegions() {
- if (!mMode.hasGestures) {
+ if (!hasGestures(mMode)) {
return;
}
@@ -268,7 +269,7 @@
| CHANGE_SUPPORTED_BOUNDS)) != 0) {
mDisplayRotation = info.rotation;
- if (mMode.hasGestures) {
+ if (hasGestures(mMode)) {
updateGestureTouchRegions();
mOrientationTouchTransformer.createOrAddTouchRegion(info);
mCurrentAppRotation = mDisplayRotation;
@@ -295,9 +296,9 @@
mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
mContext.getResources());
- if (forceRegister || (!mMode.hasGestures && newMode.hasGestures)) {
+ if (forceRegister || (!hasGestures(mMode) && hasGestures(newMode))) {
setupOrientationSwipeHandler();
- } else if (mMode.hasGestures && !newMode.hasGestures) {
+ } else if (hasGestures(mMode) && !hasGestures(newMode)) {
destroyOrientationSwipeHandlerCallback();
}
@@ -399,7 +400,7 @@
}
public int getCurrentActiveRotation() {
- if (!mMode.hasGestures) {
+ if (!hasGestures(mMode)) {
// touch rotation should always match that of display for 3 button
return mDisplayRotation;
}
@@ -416,4 +417,8 @@
public OrientationTouchTransformer getOrientationTouchTransformer() {
return mOrientationTouchTransformer;
}
+
+ private boolean hasGestures(NavigationMode mode) {
+ return mode.hasGestures || (mode == THREE_BUTTONS && Flags.threeButtonCornerSwipe());
+ }
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ead5b7b..4392255 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -82,7 +82,6 @@
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.desktopmode.IDesktopMode;
@@ -91,16 +90,18 @@
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
import com.android.wm.shell.shared.IShellTransitions;
import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
+import com.android.wm.shell.shared.split.SplitBounds;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.splitscreen.ISplitScreen;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
import com.android.wm.shell.splitscreen.ISplitSelectListener;
import com.android.wm.shell.startingsurface.IStartingWindow;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -934,6 +935,17 @@
}
}
+ /** Tells SysUI to show the expanded view. */
+ public void showExpandedView() {
+ try {
+ if (mBubbles != null) {
+ mBubbles.showExpandedView();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call showExpandedView");
+ }
+ }
+
//
// Splitscreen
//
@@ -1517,7 +1529,7 @@
// Aidl bundles need to explicitly set class loader
// https://developer.android.com/guide/components/aidl#Bundles
if (extras != null) {
- extras.setClassLoader(getClass().getClassLoader());
+ extras.setClassLoader(SplitBounds.class.getClassLoader());
}
listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps,
wallpapers, homeContentInsets, minimizedHomeBounds, extras);
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index ab80a8c..85eea3b 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -112,8 +112,9 @@
*/
public void preloadRecentsAnimation(Intent intent) {
// Pass null animation handler to indicate this start is for preloading
- UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(intent, 0, null, null, null));
+ UI_HELPER_EXECUTOR.execute(() -> {
+ ActivityManagerWrapper.getInstance().preloadRecentsActivity(intent);
+ });
}
boolean shouldIgnoreMotionEvents() {
@@ -327,63 +328,43 @@
mCallbacks.addListener(gestureState);
mCallbacks.addListener(listener);
- if (ENABLE_SHELL_TRANSITIONS) {
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
- // Use regular (non-transient) launch for all apps page to control IME.
- if (!containerInterface.allowAllAppsFromOverview()) {
- options.setTransientLaunch();
- }
- options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+ // Use regular (non-transient) launch for all apps page to control IME.
+ if (!containerInterface.allowAllAppsFromOverview()) {
+ options.setTransientLaunch();
+ }
+ options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
- // Notify taskbar that we should skip reacting to launcher visibility change to
- // avoid a jumping taskbar.
- TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
- if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
- taskbarUIController.setSkipLauncherVisibilityChange(true);
+ // Notify taskbar that we should skip reacting to launcher visibility change to
+ // avoid a jumping taskbar.
+ TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
+ if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
+ taskbarUIController.setSkipLauncherVisibilityChange(true);
- mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
- @Override
- public void onRecentsAnimationCanceled(
- @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
- taskbarUIController.setSkipLauncherVisibilityChange(false);
- }
+ mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
+ @Override
+ public void onRecentsAnimationCanceled(
+ @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ taskbarUIController.setSkipLauncherVisibilityChange(false);
+ }
- @Override
- public void onRecentsAnimationFinished(
- @NonNull RecentsAnimationController controller) {
- taskbarUIController.setSkipLauncherVisibilityChange(false);
- }
- });
- }
+ @Override
+ public void onRecentsAnimationFinished(
+ @NonNull RecentsAnimationController controller) {
+ taskbarUIController.setSkipLauncherVisibilityChange(false);
+ }
+ });
+ }
- mRecentsAnimationStartPending = getSystemUiProxy()
- .startRecentsActivity(intent, options, mCallbacks);
- if (enableHandleDelayedGestureCallbacks()) {
- ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
- "TaskAnimationManager.startRecentsAnimation(shell transition path): ")
- .append("Setting mRecentsAnimationStartPending = ")
- .append(mRecentsAnimationStartPending));
- }
- } else {
- UI_HELPER_EXECUTOR.execute(
- () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
- intent,
- eventTime,
- mCallbacks,
- result -> {
- if (enableHandleDelayedGestureCallbacks()) {
- ActiveGestureLog.INSTANCE.addLog(
- new ActiveGestureLog.CompoundString(
- "TaskAnimationManager.startRecentsAnimation")
- .append("(legacy path): Setting ")
- .append("mRecentsAnimationStartPending = ")
- .append(result));
- }
- mRecentsAnimationStartPending = result;
- },
- MAIN_EXECUTOR.getHandler()));
+ mRecentsAnimationStartPending = getSystemUiProxy()
+ .startRecentsActivity(intent, options, mCallbacks);
+ if (enableHandleDelayedGestureCallbacks()) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TaskAnimationManager.startRecentsAnimation: ")
+ .append("Setting mRecentsAnimationStartPending = ")
+ .append(mRecentsAnimationStartPending));
}
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
return mCallbacks;
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index dad34ac..9e6e2f3 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -315,12 +315,12 @@
boolean isTaskSplitNotSupported = !task.isDockable ||
(intentFlags & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
boolean hideForExistingMultiWindow = container.getDeviceProfile().isMultiWindowMode;
- boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
+ boolean isLargeTile = deviceProfile.isTablet && taskView.isLargeTile();
boolean isTaskInExpectedScrollPosition =
recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
if (notEnoughTasksToSplit || isTaskSplitNotSupported || hideForExistingMultiWindow
- || (isFocusedTask && isTaskInExpectedScrollPosition)) {
+ || (isLargeTile && isTaskInExpectedScrollPosition)) {
return null;
}
@@ -340,11 +340,11 @@
DeviceProfile deviceProfile = container.getDeviceProfile();
final TaskView taskView = taskContainer.getTaskView();
final RecentsView recentsView = taskView.getRecentsView();
- boolean isLargeTileFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
+ boolean isLargeTile = deviceProfile.isTablet && taskView.isLargeTile();
boolean isInExpectedScrollPosition =
recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
boolean shouldShowActionsButtonInstead =
- isLargeTileFocusedTask && isInExpectedScrollPosition;
+ isLargeTile && isInExpectedScrollPosition;
// No "save app pair" menu item if:
// - we are in 3p launcher
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 3c6c3e4..580dcc2 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -27,6 +27,7 @@
import com.android.launcher3.R;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.recents.data.HighResLoadingStateNotifier;
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource;
import com.android.quickstep.util.TaskKeyByLastActiveTimeCache;
import com.android.quickstep.util.TaskKeyCache;
@@ -48,7 +49,7 @@
private final boolean mEnableTaskSnapshotPreloading;
private final Context mContext;
- public static class HighResLoadingState {
+ public static class HighResLoadingState implements HighResLoadingStateNotifier {
private boolean mForceHighResThumbnails;
private boolean mVisible;
private boolean mFlingingFast;
@@ -65,11 +66,13 @@
mForceHighResThumbnails = !supportsLowResThumbnails();
}
- public void addCallback(HighResLoadingStateChangedCallback callback) {
+ @Override
+ public void addCallback(@NonNull HighResLoadingStateChangedCallback callback) {
mCallbacks.add(callback);
}
- public void removeCallback(HighResLoadingStateChangedCallback callback) {
+ @Override
+ public void removeCallback(@NonNull HighResLoadingStateChangedCallback callback) {
mCallbacks.remove(callback);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 88ab528..b321b3e 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -89,6 +89,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ConstantItem;
import com.android.launcher3.EncryptionType;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.config.FeatureFlags;
@@ -97,6 +98,7 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks;
+import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -109,6 +111,7 @@
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
+import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -672,8 +675,9 @@
private void initInputMonitor(String reason) {
disposeEventHandlers("Initializing input monitor due to: " + reason);
- if (mDeviceState.isButtonNavMode() && (!ENABLE_TRACKPAD_GESTURE.get()
- || mTrackpadsConnected.isEmpty())) {
+ if (mDeviceState.isButtonNavMode()
+ && !mDeviceState.supportsAssistantGestureInButtonNav()
+ && (!ENABLE_TRACKPAD_GESTURE.get() || mTrackpadsConnected.isEmpty())) {
return;
}
@@ -855,7 +859,9 @@
.append("); cancelling gesture."),
NAVIGATION_MODE_SWITCHED);
event.setAction(ACTION_CANCEL);
- } else if (mDeviceState.isButtonNavMode() && !isTrackpadMotionEvent(event)) {
+ } else if (mDeviceState.isButtonNavMode()
+ && !mDeviceState.supportsAssistantGestureInButtonNav()
+ && !isTrackpadMotionEvent(event)) {
ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
.append("Cannot process input event: ")
.append("using 3-button nav and event is not a trackpad event"));
@@ -901,11 +907,29 @@
boolean isOneHandedModeActive = mDeviceState.isOneHandedModeActive();
boolean isInSwipeUpTouchRegion = mRotationTouchHelper.isInSwipeUpTouchRegion(event);
TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
+ BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null;
+ boolean isOnBubbles = bubbleControllers != null
+ && BubbleBarInputConsumer.isEventOnBubbles(tac, event);
if (isInSwipeUpTouchRegion && tac != null) {
tac.closeKeyboardQuickSwitchView();
}
- if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
- || isHoverActionWithoutConsumer) {
+ if (mDeviceState.isButtonNavMode()
+ && mDeviceState.supportsAssistantGestureInButtonNav()) {
+ reasonString.append("in three button mode which supports Assistant gesture");
+ // Consume gesture event for Assistant (all other gestures should do nothing).
+ if (mDeviceState.canTriggerAssistantAction(event)) {
+ reasonString.append(" and event can trigger assistant action")
+ .append(", consuming gesture for assistant action");
+ mGestureState =
+ createGestureState(mGestureState, getTrackpadGestureType(event));
+ mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event);
+ } else {
+ reasonString.append(" but event cannot trigger Assistant")
+ .append(", consuming gesture as no-op");
+ mUncheckedConsumer = InputConsumer.NO_OP;
+ }
+ } else if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
+ || isHoverActionWithoutConsumer || isOnBubbles) {
reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion
? "one handed mode is not active and event is in swipe up region"
: "isHoverActionWithoutConsumer == true")
@@ -926,8 +950,7 @@
: "event is a trackpad multi-finger swipe")
.append(" and event can trigger assistant action")
.append(", consuming gesture for assistant action");
- mGestureState = createGestureState(mGestureState,
- getTrackpadGestureType(event));
+ mGestureState = createGestureState(mGestureState, getTrackpadGestureType(event));
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
// should not interrupt it. QuickSwitch assumes that interruption can only
// happen if the next gesture is also quick switch.
@@ -1085,6 +1108,15 @@
private InputConsumer newConsumer(
GestureState previousGestureState, GestureState newGestureState, MotionEvent event) {
+ TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
+ BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null;
+ if (bubbleControllers != null && BubbleBarInputConsumer.isEventOnBubbles(tac, event)) {
+ InputConsumer consumer = new BubbleBarInputConsumer(this, bubbleControllers,
+ mInputMonitorCompat);
+ logInputConsumerSelectionReason(consumer, newCompoundString(
+ "event is on bubbles, creating new input consumer"));
+ return consumer;
+ }
AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
if (progressProxy != null) {
InputConsumer consumer = new ProgressDelegateInputConsumer(
@@ -1149,7 +1181,6 @@
}
// If Taskbar is present, we listen for swipe or cursor hover events to unstash it.
- TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
if (tac != null && !(base instanceof AssistantInputConsumer)) {
// Present always on large screen or on small screen w/ flag
boolean useTaskbarConsumer = tac.getDeviceProfile().isTaskbarPresent
@@ -1180,7 +1211,8 @@
NavHandle navHandle = tac != null ? tac.getNavHandle()
: SystemUiProxy.INSTANCE.get(this);
if (canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()
- && navHandle.canNavHandleBeLongPressed()) {
+ && navHandle.canNavHandleBeLongPressed()
+ && !ignoreThreeFingerTrackpadForNavHandleLongPress(mGestureState)) {
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
.append(SUBSTRING_PREFIX)
@@ -1276,6 +1308,11 @@
return new CompoundString(NEWLINE_PREFIX).append(substring);
}
+ private boolean ignoreThreeFingerTrackpadForNavHandleLongPress(GestureState gestureState) {
+ return Flags.ignoreThreeFingerTrackpadForNavHandleLongPress()
+ && gestureState.isThreeFingerTrackpadGesture();
+ }
+
private void logInputConsumerSelectionReason(
InputConsumer consumer, CompoundString reasonString) {
ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
new file mode 100644
index 0000000..dbe2068
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/BubbleBarInputConsumer.java
@@ -0,0 +1,147 @@
+/*
+ * 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 android.view.MotionEvent.INVALID_POINTER_ID;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
+import com.android.launcher3.taskbar.bubbles.BubbleControllers;
+import com.android.launcher3.taskbar.bubbles.BubbleDragController;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.quickstep.InputConsumer;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Listens for touch events on the bubble bar.
+ */
+public class BubbleBarInputConsumer implements InputConsumer {
+
+ private final BubbleStashController mBubbleStashController;
+ private final BubbleBarViewController mBubbleBarViewController;
+ private final BubbleDragController mBubbleDragController;
+ private final InputMonitorCompat mInputMonitorCompat;
+
+ private boolean mSwipeUpOnBubbleHandle;
+ private boolean mPassedTouchSlop;
+
+ private final int mTouchSlop;
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final long mTimeForTap;
+ private int mActivePointerId = INVALID_POINTER_ID;
+
+ public BubbleBarInputConsumer(Context context, BubbleControllers bubbleControllers,
+ InputMonitorCompat inputMonitorCompat) {
+ mBubbleStashController = bubbleControllers.bubbleStashController;
+ mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
+ mBubbleDragController = bubbleControllers.bubbleDragController;
+ mInputMonitorCompat = inputMonitorCompat;
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mTimeForTap = ViewConfiguration.getTapTimeout();
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_BUBBLE_BAR;
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ final boolean isStashed = mBubbleStashController.isStashed();
+ final int action = ev.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+ float dX = mLastPos.x - mDownPos.x;
+ float dY = mLastPos.y - mDownPos.y;
+ if (!mPassedTouchSlop) {
+ mPassedTouchSlop = Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
+ }
+ if ((isCollapsed() || isStashed) && !mSwipeUpOnBubbleHandle && mPassedTouchSlop) {
+ boolean verticalGesture = Math.abs(dY) > Math.abs(dX);
+ if (verticalGesture && !mBubbleDragController.isDragging()) {
+ mSwipeUpOnBubbleHandle = true;
+ mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
+ // Bubbles is handling the swipe so make sure no one else gets it.
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
+ mInputMonitorCompat.pilferPointers();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ boolean isWithinTapTime = ev.getEventTime() - ev.getDownTime() <= mTimeForTap;
+ if (isWithinTapTime && !mSwipeUpOnBubbleHandle && !mPassedTouchSlop) {
+ // Taps on the handle / collapsed state should open the bar
+ if (isStashed || isCollapsed()) {
+ mBubbleStashController.showBubbleBar(/* expandBubbles= */ true);
+ }
+ }
+ break;
+ }
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ cleanupAfterMotionEvent();
+ }
+ }
+
+ private void cleanupAfterMotionEvent() {
+ mPassedTouchSlop = false;
+ mSwipeUpOnBubbleHandle = false;
+ }
+
+ private boolean isCollapsed() {
+ return mBubbleStashController.isBubbleBarVisible()
+ && !mBubbleBarViewController.isExpanded();
+ }
+
+ /**
+ * Returns whether the event is occurring on a visible bubble bar or the bar handle.
+ */
+ public static boolean isEventOnBubbles(TaskbarActivityContext tac, MotionEvent ev) {
+ if (tac == null || !tac.isBubbleBarEnabled()) {
+ return false;
+ }
+ BubbleControllers controllers = tac.getBubbleControllers();
+ if (controllers == null || !controllers.bubbleBarViewController.hasBubbles()) {
+ return false;
+ }
+ if (controllers.bubbleStashController.isStashed()
+ && controllers.bubbleStashedHandleViewController.isPresent()) {
+ return controllers.bubbleStashedHandleViewController.get().isEventOverHandle(ev);
+ } else if (controllers.bubbleBarViewController.isBubbleBarVisible()) {
+ return controllers.bubbleBarViewController.isEventOverBubbleBar(ev);
+ }
+ return false;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index f264364..14f47d1 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -58,7 +58,6 @@
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import java.util.HashMap;
@@ -278,9 +277,7 @@
}
private void endRemoteAnimation() {
- if (mHomeLaunched) {
- ActivityManagerWrapper.getInstance().cancelRecentsAnimation(false);
- } else if (mRecentsAnimationController != null) {
+ if (!mHomeLaunched && mRecentsAnimationController != null) {
mRecentsAnimationController.finishController(
false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 9a99d4a..17a97fa 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -15,9 +15,7 @@
*/
package com.android.quickstep.inputconsumers;
-import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.launcher3.Flags.enableCursorHoverStates;
@@ -43,7 +41,6 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarThresholdUtils;
import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
-import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.GestureState;
@@ -69,9 +66,6 @@
private final int mTaskbarNavThresholdY;
private final boolean mIsTaskbarAllAppsOpen;
private boolean mHasPassedTaskbarNavThreshold;
- private boolean mIsInBubbleBarArea;
- private boolean mIsVerticalGestureOverBubbleBar;
- private boolean mIsPassedBubbleBarSlop;
private final int mTouchSlop;
private final PointF mDownPos = new PointF();
@@ -159,9 +153,6 @@
if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
mTransitionCallback.onActionDown();
}
- if (mIsTransientTaskbar && isInBubbleBarArea(x)) {
- mIsInBubbleBarArea = true;
- }
break;
case MotionEvent.ACTION_POINTER_UP:
int ptrIdx = ev.getActionIndex();
@@ -185,18 +176,6 @@
float dX = mLastPos.x - mDownPos.x;
float dY = mLastPos.y - mDownPos.y;
- if (!mIsPassedBubbleBarSlop && mIsInBubbleBarArea) {
- boolean passedSlop =
- Math.abs(dY) > mTouchSlop || Math.abs(dX) > mTouchSlop;
- if (passedSlop) {
- mIsPassedBubbleBarSlop = true;
- mIsVerticalGestureOverBubbleBar = Math.abs(dY) > Math.abs(dX);
- if (mIsVerticalGestureOverBubbleBar) {
- setActive(ev);
- }
- }
- }
-
if (mIsTransientTaskbar) {
boolean passedTaskbarNavThreshold = dY < 0
&& Math.abs(dY) >= mTaskbarNavThreshold;
@@ -204,11 +183,7 @@
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold
&& !mGestureState.isInExtendedSlopRegion()) {
mHasPassedTaskbarNavThreshold = true;
- if (mIsInBubbleBarArea && mIsVerticalGestureOverBubbleBar) {
- mTaskbarActivityContext.onSwipeToOpenBubblebar();
- } else {
- mTaskbarActivityContext.onSwipeToUnstashTaskbar();
- }
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
}
if (dY < 0) {
@@ -230,41 +205,8 @@
break;
}
}
- boolean isMovingInBubbleBarArea = mIsInBubbleBarArea && ev.getAction() == ACTION_MOVE;
if (!isStashedTaskbarHovered) {
- // if we're moving in the bubble bar area but we haven't passed the slop yet, don't
- // propagate to the delegate, until we can determine the direction of the gesture.
- if (!isMovingInBubbleBarArea || mIsPassedBubbleBarSlop) {
- mDelegate.onMotionEvent(ev);
- }
- }
- } else if (mIsVerticalGestureOverBubbleBar) {
- // if we get here then this gesture is a vertical swipe over the bubble bar.
- // we're also active and there's no need to delegate any additional motion events. the
- // rest of the gesture will be handled here.
- switch (ev.getAction()) {
- case ACTION_MOVE:
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-
- float dY = mLastPos.y - mDownPos.y;
-
- // bubble bar swipe gesture uses the same threshold as the taskbar.
- boolean passedTaskbarNavThreshold = dY < 0
- && Math.abs(dY) >= mTaskbarNavThreshold;
-
- if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
- mHasPassedTaskbarNavThreshold = true;
- mTaskbarActivityContext.onSwipeToOpenBubblebar();
- }
- break;
- case ACTION_UP:
- case ACTION_CANCEL:
- cleanupAfterMotionEvent();
- break;
+ mDelegate.onMotionEvent(ev);
}
}
}
@@ -301,9 +243,6 @@
mTransitionCallback.onActionEnd();
}
mHasPassedTaskbarNavThreshold = false;
- mIsInBubbleBarArea = false;
- mIsVerticalGestureOverBubbleBar = false;
- mIsPassedBubbleBarSlop = false;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -313,23 +252,6 @@
mMotionMoveCount = 0;
}
- private boolean isInBubbleBarArea(float x) {
- if (mTaskbarActivityContext == null || !mIsTransientTaskbar) {
- return false;
- }
- BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers();
- if (controllers == null) {
- return false;
- }
- if (controllers.bubbleStashController.isStashed()
- && controllers.bubbleStashedHandleViewController.isPresent()) {
- return controllers.bubbleStashedHandleViewController.get().containsX((int) x);
- } else {
- Rect bubbleBarBounds = controllers.bubbleBarViewController.getBubbleBarBounds();
- return x >= bubbleBarBounds.left && x <= bubbleBarBounds.right;
- }
- }
-
/**
* Listen for hover events for the stashed taskbar.
*
diff --git a/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt b/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
new file mode 100644
index 0000000..df546ca
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/HighResLoadingStateNotifier.kt
@@ -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.
+ */
+
+package com.android.quickstep.recents.data
+
+import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+
+/** Notifies added callbacks that high res state has changed */
+interface HighResLoadingStateNotifier {
+ /** Adds a callback for high res loading state */
+ fun addCallback(callback: HighResLoadingStateChangedCallback)
+
+ /** Removes a callback for high res loading state */
+ fun removeCallback(callback: HighResLoadingStateChangedCallback)
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
index 4f7a541..9c4248c 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
@@ -41,10 +41,4 @@
* populated e.g. icons/thumbnails etc.
*/
fun setVisibleTasks(visibleTaskIdList: List<Int>)
-
- /**
- * Override [ThumbnailData] with a map of taskId to [ThumbnailData]. The override only applies
- * if the tasks are already visible, and will be invalidated when tasks become invisible.
- */
- fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>)
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt
index feed2fd..d2cb595 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.kt
@@ -23,6 +23,4 @@
*/
data class RecentsDeviceProfile(
val isLargeScreen: Boolean,
- val widthPx: Int,
- val heightPx: Int,
)
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
index ce39ff1..c64453d 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
@@ -26,7 +26,5 @@
RecentsDeviceProfileRepository {
override fun getRecentsDeviceProfile() =
- with(container.deviceProfile) {
- RecentsDeviceProfile(isLargeScreen = isTablet, widthPx = widthPx, heightPx = heightPx)
- }
+ with(container.deviceProfile) { RecentsDeviceProfile(isLargeScreen = isTablet) }
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangeNotifier.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangeNotifier.kt
new file mode 100644
index 0000000..6e7789d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangeNotifier.kt
@@ -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.
+ */
+
+package com.android.quickstep.recents.data
+
+import com.android.quickstep.util.TaskVisualsChangeListener
+
+/** Notifies added listeners that task visuals have changed */
+interface TaskVisualsChangeNotifier {
+ /** Adds a listener for visuals changes */
+ fun addThumbnailChangeListener(listener: TaskVisualsChangeListener)
+
+ /** Removes a listener for visuals changes */
+ fun removeThumbnailChangeListener(listener: TaskVisualsChangeListener)
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
new file mode 100644
index 0000000..a141e89
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegate.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.recents.data
+
+import android.os.UserHandle
+import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
+import com.android.quickstep.util.TaskVisualsChangeListener
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+
+/** Delegates the checking of task visuals (thumbnails, high res changes, icons) */
+interface TaskVisualsChangedDelegate :
+ TaskVisualsChangeListener, HighResLoadingStateChangedCallback {
+ /** Registers a callback for visuals relating to icons */
+ fun registerTaskIconChangedCallback(
+ taskKey: Task.TaskKey,
+ taskIconChangedCallback: TaskIconChangedCallback
+ )
+
+ /** Unregisters a callback for visuals relating to icons */
+ fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey)
+
+ /** Registers a callback for visuals relating to thumbnails */
+ fun registerTaskThumbnailChangedCallback(
+ taskKey: Task.TaskKey,
+ taskThumbnailChangedCallback: TaskThumbnailChangedCallback
+ )
+
+ /** Unregisters a callback for visuals relating to thumbnails */
+ fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey)
+
+ /** A callback for task icon changes */
+ interface TaskIconChangedCallback {
+ /** Informs the listener that the task icon has changed */
+ fun onTaskIconChanged()
+ }
+
+ /** A callback for task thumbnail changes */
+ interface TaskThumbnailChangedCallback {
+ /** Informs the listener that the task thumbnail data has changed to [thumbnailData] */
+ fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?)
+
+ /** Informs the listener that the default resolution for loading thumbnails has changed */
+ fun onHighResLoadingStateChanged()
+ }
+}
+
+class TaskVisualsChangedDelegateImpl(
+ private val taskVisualsChangeNotifier: TaskVisualsChangeNotifier,
+ private val highResLoadingStateNotifier: HighResLoadingStateNotifier,
+) : TaskVisualsChangedDelegate {
+ private val taskIconChangedCallbacks =
+ mutableMapOf<Int, Pair<Task.TaskKey, TaskIconChangedCallback>>()
+ private val taskThumbnailChangedCallbacks =
+ mutableMapOf<Int, Pair<Task.TaskKey, TaskThumbnailChangedCallback>>()
+ private var isListening = false
+
+ @Synchronized
+ private fun onCallbackRegistered() {
+ if (isListening) return
+
+ taskVisualsChangeNotifier.addThumbnailChangeListener(this)
+ highResLoadingStateNotifier.addCallback(this)
+ isListening = true
+ }
+
+ @Synchronized
+ private fun onCallbackUnregistered() {
+ if (!isListening) return
+
+ if (taskIconChangedCallbacks.size + taskThumbnailChangedCallbacks.size == 0) {
+ taskVisualsChangeNotifier.removeThumbnailChangeListener(this)
+ highResLoadingStateNotifier.removeCallback(this)
+ }
+
+ isListening = false
+ }
+
+ override fun onTaskIconChanged(taskId: Int) {
+ taskIconChangedCallbacks[taskId]?.let { (_, callback) -> callback.onTaskIconChanged() }
+ }
+
+ override fun onTaskIconChanged(pkg: String, user: UserHandle) {
+ taskIconChangedCallbacks.values
+ .filter { (taskKey, _) ->
+ pkg == taskKey.packageName && user.identifier == taskKey.userId
+ }
+ .forEach { (_, callback) -> callback.onTaskIconChanged() }
+ }
+
+ override fun onTaskThumbnailChanged(taskId: Int, thumbnailData: ThumbnailData?): Task? {
+ taskThumbnailChangedCallbacks[taskId]?.let { (_, callback) ->
+ callback.onTaskThumbnailChanged(thumbnailData)
+ }
+ return null
+ }
+
+ override fun onHighResLoadingStateChanged(enabled: Boolean) {
+ taskThumbnailChangedCallbacks.values.forEach { (_, callback) ->
+ callback.onHighResLoadingStateChanged()
+ }
+ }
+
+ override fun registerTaskIconChangedCallback(
+ taskKey: Task.TaskKey,
+ taskIconChangedCallback: TaskIconChangedCallback
+ ) {
+ taskIconChangedCallbacks[taskKey.id] = taskKey to taskIconChangedCallback
+ onCallbackRegistered()
+ }
+
+ override fun unregisterTaskIconChangedCallback(taskKey: Task.TaskKey) {
+ taskIconChangedCallbacks.remove(taskKey.id)
+ onCallbackUnregistered()
+ }
+
+ override fun registerTaskThumbnailChangedCallback(
+ taskKey: Task.TaskKey,
+ taskThumbnailChangedCallback: TaskThumbnailChangedCallback
+ ) {
+ taskThumbnailChangedCallbacks[taskKey.id] = taskKey to taskThumbnailChangedCallback
+ onCallbackRegistered()
+ }
+
+ override fun unregisterTaskThumbnailChangedCallback(taskKey: Task.TaskKey) {
+ taskThumbnailChangedCallbacks.remove(taskKey.id)
+ onCallbackUnregistered()
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 6f9d157..eb3c2d1 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -18,6 +18,8 @@
import android.graphics.drawable.Drawable
import com.android.launcher3.util.coroutines.DispatcherProvider
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
import com.android.quickstep.util.GroupTask
@@ -26,18 +28,20 @@
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
@@ -46,12 +50,12 @@
private val recentsModel: RecentTasksDataSource,
private val taskThumbnailDataSource: TaskThumbnailDataSource,
private val taskIconDataSource: TaskIconDataSource,
+ private val taskVisualsChangedDelegate: TaskVisualsChangedDelegate,
recentsCoroutineScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
) : RecentTasksRepository {
private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
- private val thumbnailOverride = MutableStateFlow(mapOf<Int, ThumbnailData>())
private val taskData =
groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } }
@@ -85,15 +89,13 @@
.distinctUntilChanged()
private val augmentedTaskData: Flow<List<Task>> =
- combine(taskData, thumbnailQueryResults, iconQueryResults, thumbnailOverride) {
+ combine(taskData, thumbnailQueryResults, iconQueryResults) {
tasks,
thumbnailQueryResults,
- iconQueryResults,
- thumbnailOverride ->
+ iconQueryResults ->
tasks.onEach { task ->
// Add retrieved thumbnails + remove unnecessary thumbnails (e.g. invisible)
- task.thumbnail =
- thumbnailOverride[task.key.id] ?: thumbnailQueryResults[task.key.id]
+ task.thumbnail = thumbnailQueryResults[task.key.id]
// TODO(b/352331675) don't load icons for DesktopTaskView
// Add retrieved icons + remove unnecessary icons
@@ -121,61 +123,76 @@
override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
this.visibleTaskIds.value = visibleTaskIdList.toSet()
- addOrUpdateThumbnailOverride(emptyMap())
- }
-
- override fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
- this.thumbnailOverride.value =
- this.thumbnailOverride.value
- .toMutableMap()
- .apply { putAll(thumbnailOverride) }
- .filterKeys(this.visibleTaskIds.value::contains)
}
/** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
- private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest = flow {
- emit(task.key.id to task.thumbnail)
- val thumbnailDataResult: ThumbnailData? =
- withContext(dispatcherProvider.main) {
- suspendCancellableCoroutine { continuation ->
- val cancellableTask =
- taskThumbnailDataSource.getThumbnailInBackground(task) {
- continuation.resume(it)
- }
- continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest = callbackFlow {
+ trySend(task.key.id to task.thumbnail)
+ trySend(task.key.id to getThumbnailFromDataSource(task))
+
+ val callback =
+ object : TaskThumbnailChangedCallback {
+ override fun onTaskThumbnailChanged(thumbnailData: ThumbnailData?) {
+ trySend(task.key.id to thumbnailData)
+ }
+
+ override fun onHighResLoadingStateChanged() {
+ launch { trySend(task.key.id to getThumbnailFromDataSource(task)) }
}
}
- emit(task.key.id to thumbnailDataResult)
+ taskVisualsChangedDelegate.registerTaskThumbnailChangedCallback(task.key, callback)
+ awaitClose { taskVisualsChangedDelegate.unregisterTaskThumbnailChangedCallback(task.key) }
}
- /** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
+ /** Flow wrapper for [TaskIconDataSource.getIconInBackground] api */
private fun getIconDataRequest(task: Task): IconDataRequest =
- flow {
- emit(task.key.id to task.getTaskIconQueryResponse())
- val iconDataResponse: TaskIconQueryResponse? =
- withContext(dispatcherProvider.main) {
- suspendCancellableCoroutine { continuation ->
- val cancellableTask =
- taskIconDataSource.getIconInBackground(task) {
- icon,
- contentDescription,
- title ->
- icon.constantState?.let {
- continuation.resume(
- TaskIconQueryResponse(
- it.newDrawable().mutate(),
- contentDescription,
- title
- )
- )
- }
- }
- continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ callbackFlow {
+ trySend(task.key.id to task.getTaskIconQueryResponse())
+ trySend(task.key.id to getIconFromDataSource(task))
+
+ val callback =
+ object : TaskIconChangedCallback {
+ override fun onTaskIconChanged() {
+ launch { trySend(task.key.id to getIconFromDataSource(task)) }
}
}
- emit(task.key.id to iconDataResponse)
+ taskVisualsChangedDelegate.registerTaskIconChangedCallback(task.key, callback)
+ awaitClose {
+ taskVisualsChangedDelegate.unregisterTaskIconChangedCallback(task.key)
+ }
}
.distinctUntilChanged()
+
+ private suspend fun getThumbnailFromDataSource(task: Task) =
+ withContext(dispatcherProvider.main) {
+ suspendCancellableCoroutine { continuation ->
+ val cancellableTask =
+ taskThumbnailDataSource.getThumbnailInBackground(task) {
+ continuation.resume(it)
+ }
+ continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ }
+ }
+
+ private suspend fun getIconFromDataSource(task: Task) =
+ withContext(dispatcherProvider.main) {
+ suspendCancellableCoroutine { continuation ->
+ val cancellableTask =
+ taskIconDataSource.getIconInBackground(task) { icon, contentDescription, title
+ ->
+ icon.constantState?.let {
+ continuation.resume(
+ TaskIconQueryResponse(
+ it.newDrawable().mutate(),
+ contentDescription,
+ title
+ )
+ )
+ }
+ }
+ continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ }
+ }
}
data class TaskIconQueryResponse(
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index d8156b1..0a5544f 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -22,12 +22,13 @@
import com.android.launcher3.util.coroutines.ProductionDispatchers
import com.android.quickstep.RecentsModel
import com.android.quickstep.recents.data.RecentTasksRepository
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegateImpl
import com.android.quickstep.recents.data.TasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
import com.android.quickstep.recents.usecase.SysUiStatusNavFlagsUseCase
import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.quickstep.task.thumbnail.GetSplashSizeUseCase
import com.android.quickstep.task.thumbnail.SplashAlphaUseCase
import com.android.quickstep.task.thumbnail.TaskThumbnailViewData
import com.android.quickstep.task.viewmodel.TaskContainerData
@@ -61,14 +62,22 @@
val recentsCoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("RecentsView"))
set(CoroutineScope::class.java.simpleName, recentsCoroutineScope)
+ val recentsModel = RecentsModel.INSTANCE.get(appContext)
+ val taskVisualsChangedDelegate =
+ TaskVisualsChangedDelegateImpl(
+ recentsModel,
+ recentsModel.thumbnailCache.highResLoadingState
+ )
+ set(TaskVisualsChangedDelegate::class.java.simpleName, taskVisualsChangedDelegate)
// Create RecentsTaskRepository singleton
val recentTasksRepository: RecentTasksRepository =
- with(RecentsModel.INSTANCE.get(appContext)) {
+ with(recentsModel) {
TasksRepository(
this,
thumbnailCache,
iconCache,
+ taskVisualsChangedDelegate,
recentsCoroutineScope,
ProductionDispatchers
)
@@ -156,6 +165,7 @@
thumbnailCache,
iconCache,
get(),
+ get(),
ProductionDispatchers
)
}
@@ -176,7 +186,6 @@
getThumbnailPositionUseCase = inject(),
tasksRepository = inject(),
splashAlphaUseCase = inject(scopeId),
- getSplashSizeUseCase = inject(scopeId),
)
TaskOverlayViewModel::class.java -> {
val task = extras["Task"] as Task
@@ -204,12 +213,6 @@
tasksRepository = inject(),
rotationStateRepository = inject(),
)
- GetSplashSizeUseCase::class.java ->
- GetSplashSizeUseCase(
- taskThumbnailViewData = inject(scopeId),
- taskViewData = inject(scopeId, extras),
- deviceProfileRepository = inject(),
- )
else -> {
log("Factory for ${modelClass.simpleName} not defined!", Log.ERROR)
error("Factory for ${modelClass.simpleName} not defined!")
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index b1f46a3..1716f2e 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -58,10 +58,6 @@
recentsViewData.thumbnailSplashProgress.value = taskThumbnailSplashAlpha
}
- fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
- recentsTasksRepository.addOrUpdateThumbnailOverride(thumbnailOverride)
- }
-
suspend fun waitForThumbnailsToUpdate(updatedThumbnails: Map<Int, ThumbnailData>) {
combine(
updatedThumbnails.map {
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCase.kt b/quickstep/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCase.kt
deleted file mode 100644
index 145957a..0000000
--- a/quickstep/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCase.kt
+++ /dev/null
@@ -1,46 +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.quickstep.task.thumbnail
-
-import android.graphics.Point
-import android.graphics.drawable.Drawable
-import com.android.quickstep.recents.data.RecentsDeviceProfileRepository
-import com.android.quickstep.task.viewmodel.TaskViewData
-import kotlin.math.min
-
-class GetSplashSizeUseCase(
- private val taskThumbnailViewData: TaskThumbnailViewData,
- private val taskViewData: TaskViewData,
- private val deviceProfileRepository: RecentsDeviceProfileRepository,
-) {
- fun execute(splashImage: Drawable): Point {
- val recentsDeviceProfile = deviceProfileRepository.getRecentsDeviceProfile()
- val screenWidth = recentsDeviceProfile.widthPx
- val screenHeight = recentsDeviceProfile.heightPx
- val scaleAtFullscreen =
- min(
- screenWidth / taskThumbnailViewData.width.value,
- screenHeight / taskThumbnailViewData.height.value,
- )
- val scaleFactor: Float = 1f / taskViewData.nonGridScale.value / scaleAtFullscreen
- return Point(
- (splashImage.intrinsicWidth * scaleFactor / taskThumbnailViewData.scaleX.value).toInt(),
- (splashImage.intrinsicHeight * scaleFactor / taskThumbnailViewData.scaleY.value)
- .toInt(),
- )
- }
-}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index a6be9f6..36a86f2 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -17,7 +17,6 @@
package com.android.quickstep.task.thumbnail
import android.graphics.Bitmap
-import android.graphics.Point
import android.graphics.drawable.Drawable
import android.view.Surface
import androidx.annotation.ColorInt
@@ -31,7 +30,7 @@
data class SnapshotSplash(
val snapshot: Snapshot,
- val splash: Splash,
+ val splash: Drawable?,
) : TaskThumbnailUiState()
data class Snapshot(
@@ -39,9 +38,4 @@
@Surface.Rotation val thumbnailRotation: Int,
@ColorInt val backgroundColor: Int
)
-
- data class Splash(
- val icon: Drawable?,
- val size: Point,
- )
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 41aee52..0279818 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -24,11 +24,9 @@
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
-import android.widget.FrameLayout
-import android.widget.ImageView
import androidx.annotation.ColorInt
-import androidx.core.view.isVisible
-import androidx.core.view.updateLayoutParams
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isInvisible
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.ViewPool
@@ -41,6 +39,7 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
import com.android.quickstep.util.TaskCornerRadius
+import com.android.quickstep.views.FixedSizeImageView
import com.android.systemui.shared.system.QuickStepContract
import kotlin.math.abs
import kotlinx.coroutines.CoroutineName
@@ -51,7 +50,7 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
+class TaskThumbnailView : ConstraintLayout, ViewPool.Reusable {
private val viewData: TaskThumbnailViewData by RecentsDependencies.inject(this)
private val viewModel: TaskThumbnailViewModel by RecentsDependencies.inject(this)
@@ -60,9 +59,9 @@
private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) }
- private val thumbnailView: ImageView by lazy { findViewById(R.id.task_thumbnail) }
- private val splashContainer: FrameLayout by lazy { findViewById(R.id.splash_container) }
- private val splashIcon: ImageView by lazy { findViewById(R.id.splash_icon) }
+ private val thumbnailView: FixedSizeImageView by lazy { findViewById(R.id.task_thumbnail) }
+ private val splashBackground: View by lazy { findViewById(R.id.splash_background) }
+ private val splashIcon: FixedSizeImageView by lazy { findViewById(R.id.splash_icon) }
private var uiState: TaskThumbnailUiState = Uninitialized
private var inheritedScale: Float = 1f
@@ -107,7 +106,10 @@
.onEach { dimProgress -> scrimView.alpha = dimProgress }
.launchIn(viewAttachedScope)
viewModel.splashAlpha
- .onEach { splashAlpha -> splashContainer.alpha = splashAlpha }
+ .onEach { splashAlpha ->
+ splashBackground.alpha = splashAlpha
+ splashIcon.alpha = splashAlpha
+ }
.launchIn(viewAttachedScope)
viewModel.cornerRadiusProgress.onEach { invalidateOutline() }.launchIn(viewAttachedScope)
viewModel.inheritedScale
@@ -152,15 +154,13 @@
override fun setScaleX(scaleX: Float) {
super.setScaleX(scaleX)
- viewData.scaleX.value = scaleX
- // Splash icon should ignore scale
+ // Splash icon should ignore scale on TTV
splashIcon.scaleX = 1 / scaleX
}
override fun setScaleY(scaleY: Float) {
super.setScaleY(scaleY)
- viewData.scaleY.value = scaleY
- // Splash icon should ignore scale
+ // Splash icon should ignore scale on TTV
splashIcon.scaleY = 1 / scaleY
}
@@ -173,9 +173,10 @@
}
private fun resetViews() {
- liveTileView.isVisible = false
- thumbnailView.isVisible = false
- splashContainer.alpha = 0f
+ liveTileView.isInvisible = true
+ thumbnailView.isInvisible = true
+ splashBackground.alpha = 0f
+ splashIcon.alpha = 0f
scrimView.alpha = 0f
setBackgroundColor(Color.BLACK)
}
@@ -185,25 +186,20 @@
}
private fun drawLiveWindow() {
- liveTileView.isVisible = true
+ liveTileView.isInvisible = false
}
private fun drawSnapshotSplash(snapshotSplash: SnapshotSplash) {
drawSnapshot(snapshotSplash.snapshot)
- splashContainer.isVisible = true
- splashContainer.setBackgroundColor(snapshotSplash.snapshot.backgroundColor)
- splashIcon.setImageDrawable(snapshotSplash.splash.icon)
- splashIcon.updateLayoutParams<LayoutParams> {
- width = snapshotSplash.splash.size.x
- height = snapshotSplash.splash.size.y
- }
+ splashBackground.setBackgroundColor(snapshotSplash.snapshot.backgroundColor)
+ splashIcon.setImageDrawable(snapshotSplash.splash)
}
private fun drawSnapshot(snapshot: Snapshot) {
drawBackground(snapshot.backgroundColor)
thumbnailView.setImageBitmap(snapshot.bitmap)
- thumbnailView.isVisible = true
+ thumbnailView.isInvisible = false
setImageMatrix()
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt
index 1f8c0bc..3502029 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt
@@ -21,6 +21,4 @@
class TaskThumbnailViewData {
val width = MutableStateFlow(0)
val height = MutableStateFlow(0)
- val scaleX = MutableStateFlow(1f)
- val scaleY = MutableStateFlow(1f)
}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
index 4e29840..b1bb65e 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
@@ -19,20 +19,17 @@
import android.annotation.ColorInt
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.graphics.Matrix
-import android.graphics.Point
import androidx.core.graphics.ColorUtils
import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState
import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.quickstep.task.thumbnail.GetSplashSizeUseCase
import com.android.quickstep.task.thumbnail.SplashAlphaUseCase
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Splash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.systemui.shared.recents.model.Task
import kotlin.math.max
@@ -55,7 +52,6 @@
private val tasksRepository: RecentTasksRepository,
private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
private val splashAlphaUseCase: SplashAlphaUseCase,
- private val getSplashSizeUseCase: GetSplashSizeUseCase,
) {
private val task = MutableStateFlow<Flow<Task?>>(flowOf(null))
private val splashProgress = MutableStateFlow(flowOf(0f))
@@ -100,7 +96,7 @@
isBackgroundOnly(taskVal) ->
BackgroundOnly(taskVal.colorBackground.removeAlpha())
isSnapshotSplashState(taskVal) ->
- SnapshotSplash(createSnapshotState(taskVal), createSplashState(taskVal))
+ SnapshotSplash(createSnapshotState(taskVal), taskVal.icon)
else -> Uninitialized
}
}
@@ -139,12 +135,6 @@
return Snapshot(bitmap, thumbnailData.rotation, task.colorBackground.removeAlpha())
}
- private fun createSplashState(task: Task): Splash {
- val taskIcon = task.icon
- val size = if (taskIcon == null) Point() else getSplashSizeUseCase.execute(taskIcon)
- return Splash(taskIcon, size)
- }
-
@ColorInt private fun Int.removeAlpha(): Int = ColorUtils.setAlphaComponent(this, 0xff)
private companion object {
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
index 07dfc29..7a9ecf2 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
@@ -23,8 +23,6 @@
// This is typically a View concern but it is used to invalidate rendering in other Views
val scale = MutableStateFlow(1f)
- val nonGridScale = MutableStateFlow(1f)
-
// TODO(b/331753115): This property should not be in TaskViewData once TaskView is MVVM.
/** Whether outline of TaskView is formed by outline thumbnail view(s). */
val isOutlineFormedByThumbnailView: Boolean = taskViewType != TaskViewType.DESKTOP
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewModel.kt
index 30ee360..ec75d59 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewModel.kt
@@ -22,8 +22,4 @@
fun updateScale(scale: Float) {
taskViewData.scale.value = scale
}
-
- fun updateNonGridScale(nonGridScale: Float) {
- taskViewData.nonGridScale.value = nonGridScale
- }
}
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index ac4032c..e1013db 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -242,8 +242,7 @@
WorkspaceItemInfo app2 = appPairIcon.getInfo().getSecondApp();
ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user);
ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user);
- mSplitSelectStateController.setLaunchingCuj(cuj);
- InteractionJankMonitorWrapper.begin(appPairIcon, cuj);
+ mSplitSelectStateController.setLaunchingCuj(appPairIcon, cuj);
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
Arrays.asList(app1Key, app2Key),
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index 0b05c2e..63fe017 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -33,8 +34,10 @@
public static final int INDEX_RECENTS_FADE_ANIM = AtomicAnimationFactory.NEXT_INDEX + 0;
public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
+ public static final int INDEX_RECENTS_ATTACHED_ALPHA_ANIM =
+ AtomicAnimationFactory.NEXT_INDEX + 2;
- private static final int MY_ANIM_COUNT = 2;
+ private static final int MY_ANIM_COUNT = 3;
protected final CONTAINER mContainer;
@@ -50,6 +53,7 @@
ObjectAnimator alpha = ObjectAnimator.ofFloat(mContainer.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
return alpha;
+ case INDEX_RECENTS_ATTACHED_ALPHA_ANIM:
case INDEX_RECENTS_TRANSLATE_X_ANIM: {
RecentsView rv = mContainer.getOverviewPanel();
return new SpringAnimationBuilder(mContainer)
@@ -57,7 +61,8 @@
.setDampingRatio(0.8f)
.setStiffness(250)
.setValues(values)
- .build(rv, ADJACENT_PAGE_HORIZONTAL_OFFSET);
+ .build(rv, index == INDEX_RECENTS_ATTACHED_ALPHA_ANIM
+ ? RUNNING_TASK_ATTACH_ALPHA : ADJACENT_PAGE_HORIZONTAL_OFFSET);
}
default:
return super.createStateElementAnimation(index, values);
diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
new file mode 100644
index 0000000..0a01d8b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
@@ -0,0 +1,71 @@
+/*
+ * 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 com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.quickstep.views.DesktopTaskView
+import com.android.quickstep.views.TaskView
+import com.android.quickstep.views.TaskViewType
+
+/**
+ * Helper class for [com.android.quickstep.views.RecentsView]. This util class contains refactored
+ * and extracted functions from RecentsView to facilitate the implementation of unit tests.
+ */
+class RecentsViewUtils {
+
+ /**
+ * Sort task groups to move desktop tasks to the end of the list.
+ *
+ * @param tasks List of group tasks to be sorted.
+ * @return Sorted list of GroupTasks to be used in the RecentsView.
+ */
+ fun sortDesktopTasksToFront(tasks: List<GroupTask>): List<GroupTask> {
+ val (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ return otherTasks + desktopTasks
+ }
+
+ fun getFocusedTaskIndex(taskGroups: List<GroupTask>): Int {
+ // The focused task index is placed after the desktop tasks views.
+ return if (enableLargeDesktopWindowingTile()) {
+ taskGroups.count { it.taskViewType == TaskViewType.DESKTOP }
+ } else {
+ 0
+ }
+ }
+
+ /**
+ * Counts [numChildren] that are [DesktopTaskView] instances.
+ *
+ * @param numChildren Quantity of children to transverse
+ * @param getTaskViewAt Function that provides a TaskView given an index
+ */
+ fun getDesktopTaskViewCount(numChildren: Int, getTaskViewAt: (Int) -> TaskView?): Int =
+ (0 until numChildren).count { getTaskViewAt(it) is DesktopTaskView }
+
+ /**
+ * Returns the first TaskView that should be displayed as a large tile.
+ *
+ * @param numChildren Quantity of children to transverse
+ * @param getTaskViewAt Function that provides a TaskView given an index
+ */
+ fun getFirstLargeTaskView(numChildren: Int, getTaskViewAt: (Int) -> TaskView?): TaskView? {
+ return (0 until numChildren).firstNotNullOfOrNull { index ->
+ val taskView = getTaskViewAt(index)
+ if (taskView?.isLargeTile == true) taskView else null
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
index d58cb91..d982e81 100644
--- a/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenUtils.kt
@@ -17,14 +17,13 @@
package com.android.quickstep.util
import android.util.Log
-import android.view.WindowManager
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionInfo.FLAG_FIRST_CUSTOM
import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.wm.shell.util.SplitBounds
+import com.android.wm.shell.shared.split.SplitBounds
import java.lang.IllegalStateException
class SplitScreenUtils {
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 2ff8e45..ae6757f 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -55,11 +55,11 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceControl;
+import android.view.View;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.RemoteTransition;
import android.window.RemoteTransitionStub;
@@ -97,7 +97,6 @@
import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.SplitInstructionsView;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
@@ -150,9 +149,10 @@
/**
* Should be a constant from {@link com.android.internal.jank.Cuj} or -1, does not need to be
- * set for all launches.
+ * set for all launches. Used in conjunction with {@link #mLaunchingViewCuj} below.
*/
private int mLaunchCuj = -1;
+ private View mLaunchingViewCuj;
private FloatingTaskView mFirstFloatingTaskView;
private SplitInstructionsView mSplitInstructionsView;
@@ -652,7 +652,12 @@
return mSplitAnimationController;
}
- public void setLaunchingCuj(int launchCuj) {
+ /**
+ * Set params to invoke a trace session for the given view and CUJ when we begin animating the
+ * split launch AFTER we get a response from Shell.
+ */
+ public void setLaunchingCuj(View launchingView, int launchCuj) {
+ mLaunchingViewCuj = launchingView;
mLaunchCuj = launchCuj;
}
@@ -690,6 +695,9 @@
&& mLaunchingTaskView.getRecentsView() != null
&& mLaunchingTaskView.getRecentsView().isTaskViewVisible(
mLaunchingTaskView);
+ if (mLaunchingViewCuj != null && mLaunchCuj != -1) {
+ InteractionJankMonitorWrapper.begin(mLaunchingViewCuj, mLaunchCuj);
+ }
mSplitAnimationController.playSplitLaunchAnimation(
shouldLaunchFromTaskView ? mLaunchingTaskView : null,
mLaunchingIconView,
@@ -752,6 +760,7 @@
InteractionJankMonitorWrapper.end(mLaunchCuj);
}
mLaunchCuj = -1;
+ mLaunchingViewCuj = null;
if (mSessionInstanceIds != null) {
mStatsLogManager.logger()
@@ -883,14 +892,17 @@
DesktopSplitRecentsAnimationListener listener =
new DesktopSplitRecentsAnimationListener(splitPosition, taskBounds);
- MAIN_EXECUTOR.execute(() -> {
- callbacks.addListener(listener);
- UI_HELPER_EXECUTOR.execute(
- // Transition from app to enter stage split in launcher with
- // recents animation.
- () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
- mOverviewComponentObserver.getOverviewIntent(),
- SystemClock.uptimeMillis(), callbacks, null, null));
+ callbacks.addListener(listener);
+ UI_HELPER_EXECUTOR.execute(() -> {
+ // Transition from app to enter stage split in launcher with recents animation
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+ options.setTransientLaunch();
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
+ .startRecentsActivity(
+ mOverviewComponentObserver.getOverviewIntent(), options,
+ callbacks);
});
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 27fb31d..1c417eb 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -27,9 +27,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.SystemClock;
import androidx.annotation.BinderThread;
@@ -91,12 +91,17 @@
MAIN_EXECUTOR.execute(() -> {
callbacks.addListener(listener);
- UI_HELPER_EXECUTOR.execute(
- // Transition from fullscreen app to enter stage split in launcher with
- // recents animation.
- () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
- mOverviewComponentObserver.getOverviewIntent(),
- SystemClock.uptimeMillis(), callbacks, null, null));
+ UI_HELPER_EXECUTOR.execute(() -> {
+ // Transition from fullscreen app to enter stage split in launcher with
+ // recents animation
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+ options.setTransientLaunch();
+ SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
+ .startRecentsActivity(mOverviewComponentObserver.getOverviewIntent(),
+ ActivityOptions.makeBasic(), callbacks);
+ });
});
}
@@ -110,17 +115,17 @@
private final boolean mLeftOrTop;
private final Rect mTempRect = new Rect();
+ private final ActivityManager.RunningTaskInfo mRunningTaskInfo;
private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
mLeftOrTop = leftOrTop;
+ mRunningTaskInfo = ActivityManagerWrapper.getInstance().getRunningTask();
}
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
- ActivityManager.RunningTaskInfo runningTaskInfo =
- ActivityManagerWrapper.getInstance().getRunningTask();
- mController.setInitialTaskSelect(runningTaskInfo,
+ mController.setInitialTaskSelect(mRunningTaskInfo,
mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
null /* itemInfo */,
mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
@@ -136,9 +141,9 @@
RectF startingTaskRect = new RectF();
final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
mLauncher, mLauncher.getDragLayer(),
- controller.screenshotTask(runningTaskInfo.taskId).getThumbnail(),
+ controller.screenshotTask(mRunningTaskInfo.taskId).getThumbnail(),
null /* icon */, startingTaskRect);
- Task task = Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
+ Task task = Task.from(new Task.TaskKey(mRunningTaskInfo), mRunningTaskInfo,
false /* isLocked */);
RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
.getIconCache()
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 56e91ed..828322b 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -39,7 +39,7 @@
import com.android.quickstep.TaskAnimationManager;
import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.wm.shell.pip.PipContentOverlay;
+import com.android.wm.shell.shared.pip.PipContentOverlay;
/**
* Subclass of {@link RectFSpringAnim} that animates an Activity to PiP (picture-in-picture) window
diff --git a/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
index 66bff73..519ef60 100644
--- a/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
+++ b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
+import android.annotation.NonNull;
import android.os.UserHandle;
import com.android.systemui.shared.recents.model.Task;
@@ -36,7 +37,7 @@
/**
* Called when the icon for a task changes
*/
- default void onTaskIconChanged(String pkg, UserHandle user) {}
+ default void onTaskIconChanged(@NonNull String pkg, @NonNull UserHandle user) {}
/**
* Called when the icon for a task changes
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 8e48f22..41add54 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.views
+import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Point
import android.graphics.PointF
@@ -23,9 +24,9 @@
import android.graphics.drawable.shapes.RoundRectShape
import android.util.AttributeSet
import android.util.Log
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updateLayoutParams
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
@@ -125,11 +126,7 @@
snapshotView,
// Add snapshotView to the front after initial views e.g. icon and
// background.
- childCountAtInflation,
- LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
- )
+ childCountAtInflation
)
TaskContainer(
this,
@@ -158,28 +155,37 @@
}
}
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val containerWidth = MeasureSpec.getSize(widthMeasureSpec)
- var containerHeight = MeasureSpec.getSize(heightMeasureSpec)
- setMeasuredDimension(containerWidth, containerHeight)
-
+ @SuppressLint("RtlHardcoded")
+ override fun updateTaskSize(
+ lastComputedTaskSize: Rect,
+ lastComputedGridTaskSize: Rect,
+ lastComputedCarouselTaskSize: Rect
+ ) {
+ super.updateTaskSize(
+ lastComputedTaskSize,
+ lastComputedGridTaskSize,
+ lastComputedCarouselTaskSize
+ )
if (taskContainers.isEmpty()) {
return
}
val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
- containerHeight -= thumbnailTopMarginPx
+
+ val containerWidth = layoutParams.width
+ val containerHeight = layoutParams.height - thumbnailTopMarginPx
BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
+
val windowWidth = tempPointF.x.toInt()
val windowHeight = tempPointF.y.toInt()
val scaleWidth = containerWidth / windowWidth.toFloat()
val scaleHeight = containerHeight / windowHeight.toFloat()
+
if (DEBUG) {
Log.d(
TAG,
- "onMeasure: container=[$containerWidth,$containerHeight] " +
+ "onMeasure: container=[$containerWidth,$containerHeight]" +
"window=[$windowWidth,$windowHeight] scale=[$scaleWidth,$scaleHeight]"
)
}
@@ -195,27 +201,26 @@
right = windowWidth / 4
bottom = windowHeight / 4
}
- val thumbWidth = (taskSize.width() * scaleWidth).toInt()
- val thumbHeight = (taskSize.height() * scaleHeight).toInt()
- it.snapshotView.measure(
- MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY)
- )
+ val positionInParent = it.task.positionInParent ?: ORIGIN
// Position the task to the same position as it would be on the desktop
- val positionInParent = it.task.positionInParent ?: ORIGIN
- val taskX = (positionInParent.x * scaleWidth).toInt()
- var taskY = (positionInParent.y * scaleHeight).toInt()
- // move task down by margin size
- taskY += thumbnailTopMarginPx
- it.snapshotView.x = taskX.toFloat()
- it.snapshotView.y = taskY.toFloat()
+ it.snapshotView.updateLayoutParams<LayoutParams> {
+ gravity = Gravity.LEFT or Gravity.TOP
+ width = (taskSize.width() * scaleWidth).toInt()
+ height = (taskSize.height() * scaleHeight).toInt()
+ leftMargin = (positionInParent.x * scaleWidth).toInt()
+ topMargin =
+ (positionInParent.y * scaleHeight).toInt() +
+ container.deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
if (DEBUG) {
- Log.d(
- TAG,
- "onMeasure: task=${it.task.key} thumb=[$thumbWidth,$thumbHeight]" +
- " pos=[$taskX,$taskY]"
- )
+ with(it.snapshotView.layoutParams as LayoutParams) {
+ Log.d(
+ TAG,
+ "onMeasure: task=${it.task.key} size=[$width,$height]" +
+ " margin=[$leftMargin,$topMargin]"
+ )
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
deleted file mode 100644
index 9f268a0..0000000
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
-
-import static com.android.launcher3.Utilities.prefixTextWithIcon;
-import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
-
-import android.app.ActivityOptions;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.AppUsageLimit;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.icu.text.MeasureFormat;
-import android.icu.text.MeasureFormat.FormatWidth;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Pair;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.time.Duration;
-import java.util.Locale;
-
-public final class DigitalWellBeingToast {
-
- private static final float THRESHOLD_LEFT_ICON_ONLY = 0.4f;
- private static final float THRESHOLD_RIGHT_ICON_ONLY = 0.6f;
-
- /** Will span entire width of taskView with full text */
- private static final int SPLIT_BANNER_FULLSCREEN = 0;
- /** Used for grid task view, only showing icon and time */
- private static final int SPLIT_GRID_BANNER_LARGE = 1;
- /** Used for grid task view, only showing icon */
- private static final int SPLIT_GRID_BANNER_SMALL = 2;
-
- @IntDef(value = {
- SPLIT_BANNER_FULLSCREEN,
- SPLIT_GRID_BANNER_LARGE,
- SPLIT_GRID_BANNER_SMALL,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface SplitBannerConfig {
- }
-
- static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
- static final int MINUTE_MS = 60000;
-
- private static final String TAG = "DigitalWellBeingToast";
-
- private final RecentsViewContainer mContainer;
- private final TaskView mTaskView;
- private final LauncherApps mLauncherApps;
-
- private final int mBannerHeight;
-
- private Task mTask;
- private boolean mHasLimit;
-
- private long mAppRemainingTimeMs;
- @Nullable
- private View mBanner;
- private ViewOutlineProvider mOldBannerOutlineProvider;
- private float mBannerOffsetPercentage;
- @Nullable
- private SplitBounds mSplitBounds;
- private float mSplitOffsetTranslationY;
- private float mSplitOffsetTranslationX;
-
- private boolean mIsDestroyed = false;
-
- public DigitalWellBeingToast(RecentsViewContainer container, TaskView taskView) {
- mContainer = container;
- mTaskView = taskView;
- mLauncherApps = container.asContext().getSystemService(LauncherApps.class);
- mBannerHeight = container.asContext().getResources().getDimensionPixelSize(
- R.dimen.digital_wellbeing_toast_height);
- }
-
- private void setNoLimit() {
- mHasLimit = false;
- mTaskView.setContentDescription(mTask.titleDescription);
- replaceBanner(null);
- mAppRemainingTimeMs = -1;
- }
-
- private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
- mAppRemainingTimeMs = appRemainingTimeMs;
- mHasLimit = true;
- TextView toast = mContainer.getViewCache().getView(R.layout.digital_wellbeing_toast,
- mContainer.asContext(), mTaskView);
- toast.setText(prefixTextWithIcon(mContainer.asContext(), R.drawable.ic_hourglass_top,
- getText()));
- toast.setOnClickListener(this::openAppUsageSettings);
- replaceBanner(toast);
-
- mTaskView.setContentDescription(
- getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs));
- }
-
- public String getText() {
- return getText(mAppRemainingTimeMs, false /* forContentDesc */);
- }
-
- public boolean hasLimit() {
- return mHasLimit;
- }
-
- public void initialize(Task task) {
- if (mIsDestroyed) {
- throw new IllegalStateException("Cannot re-initialize a destroyed toast");
- }
- mTask = task;
- ORDERED_BG_EXECUTOR.execute(() -> {
- AppUsageLimit usageLimit = null;
- try {
- usageLimit = mLauncherApps.getAppUsageLimit(
- mTask.getTopComponent().getPackageName(),
- UserHandle.of(mTask.key.userId));
- } catch (Exception e) {
- Log.e(TAG, "Error initializing digital well being toast", e);
- }
- final long appUsageLimitTimeMs =
- usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
- final long appRemainingTimeMs =
- usageLimit != null ? usageLimit.getUsageRemaining() : -1;
-
- mTaskView.post(() -> {
- if (mIsDestroyed) {
- return;
- }
- if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
- setNoLimit();
- } else {
- setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
- }
- });
- });
- }
-
- /**
- * Mark the DWB toast as destroyed and remove banner from TaskView.
- */
- public void destroy() {
- mIsDestroyed = true;
- mTaskView.post(() -> replaceBanner(null));
- }
-
- public void setSplitBounds(@Nullable SplitBounds splitBounds) {
- mSplitBounds = splitBounds;
- }
-
- private @SplitBannerConfig int getSplitBannerConfig() {
- if (mSplitBounds == null
- || !mContainer.getDeviceProfile().isTablet
- || mTaskView.isFocusedTask()) {
- return SPLIT_BANNER_FULLSCREEN;
- }
-
- // For portrait grid only height of task changes, not width. So we keep the text the same
- if (!mContainer.getDeviceProfile().isLeftRightSplit) {
- return SPLIT_GRID_BANNER_LARGE;
- }
-
- // For landscape grid, for 30% width we only show icon, otherwise show icon and time
- if (mTask.key.id == mSplitBounds.leftTopTaskId) {
- return mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY
- ? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
- } else {
- return mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY
- ? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
- }
- }
-
- private String getReadableDuration(
- Duration duration,
- @StringRes int durationLessThanOneMinuteStringId) {
- int hours = Math.toIntExact(duration.toHours());
- int minutes = Math.toIntExact(duration.minusHours(hours).toMinutes());
-
- // Apply FormatWidth.WIDE if both the hour part and the minute part are non-zero.
- if (hours > 0 && minutes > 0) {
- return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.NARROW)
- .formatMeasures(
- new Measure(hours, MeasureUnit.HOUR),
- new Measure(minutes, MeasureUnit.MINUTE));
- }
-
- // Apply FormatWidth.WIDE if only the hour part is non-zero (unless forced).
- if (hours > 0) {
- return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
- new Measure(hours, MeasureUnit.HOUR));
- }
-
- // Apply FormatWidth.WIDE if only the minute part is non-zero (unless forced).
- if (minutes > 0) {
- return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
- new Measure(minutes, MeasureUnit.MINUTE));
- }
-
- // Use a specific string for usage less than one minute but non-zero.
- if (duration.compareTo(Duration.ZERO) > 0) {
- return mContainer.asContext().getString(durationLessThanOneMinuteStringId);
- }
-
- // Otherwise, return 0-minute string.
- return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
- new Measure(0, MeasureUnit.MINUTE));
- }
-
- /**
- * Returns text to show for the banner depending on {@link #getSplitBannerConfig()}
- * If {@param forContentDesc} is {@code true}, this will always return the full
- * string corresponding to {@link #SPLIT_BANNER_FULLSCREEN}
- */
- private String getText(long remainingTime, boolean forContentDesc) {
- final Duration duration = Duration.ofMillis(
- remainingTime > MINUTE_MS ?
- (remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
- remainingTime);
- String readableDuration = getReadableDuration(duration,
- R.string.shorter_duration_less_than_one_minute
- /* forceFormatWidth */);
- @SplitBannerConfig int splitBannerConfig = getSplitBannerConfig();
- if (forContentDesc || splitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
- return mContainer.asContext().getString(
- R.string.time_left_for_app,
- readableDuration);
- }
-
- if (splitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
- // show no text
- return "";
- } else { // SPLIT_GRID_BANNER_LARGE
- // only show time
- return readableDuration;
- }
- }
-
- public void openAppUsageSettings(View view) {
- final Intent intent = new Intent(OPEN_APP_USAGE_SETTINGS_TEMPLATE)
- .putExtra(Intent.EXTRA_PACKAGE_NAME,
- mTask.getTopComponent().getPackageName()).addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- try {
- final RecentsViewContainer container =
- RecentsViewContainer.containerFromContext(view.getContext());
- final ActivityOptions options = ActivityOptions.makeScaleUpAnimation(
- view, 0, 0,
- view.getWidth(), view.getHeight());
- container.asContext().startActivity(intent, options.toBundle());
-
- // TODO: add WW logging on the app usage settings click.
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "Failed to open app usage settings for task "
- + mTask.getTopComponent().getPackageName(), e);
- }
- }
-
- private String getContentDescriptionForTask(
- Task task, long appUsageLimitTimeMs, long appRemainingTimeMs) {
- return appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0 ?
- mContainer.asContext().getString(
- R.string.task_contents_description_with_remaining_time,
- task.titleDescription,
- getText(appRemainingTimeMs, true /* forContentDesc */)) :
- task.titleDescription;
- }
-
- private void replaceBanner(@Nullable View view) {
- resetOldBanner();
- setBanner(view);
- }
-
- private void resetOldBanner() {
- if (mBanner != null) {
- mBanner.setOutlineProvider(mOldBannerOutlineProvider);
- mTaskView.removeView(mBanner);
- mBanner.setOnClickListener(null);
- mContainer.getViewCache().recycleView(R.layout.digital_wellbeing_toast, mBanner);
- }
- }
-
- private void setBanner(@Nullable View view) {
- mBanner = view;
- if (mBanner != null && mTaskView.getRecentsView() != null) {
- setupAndAddBanner();
- setBannerOutline();
- }
- }
-
- private void setupAndAddBanner() {
- FrameLayout.LayoutParams layoutParams =
- (FrameLayout.LayoutParams) mBanner.getLayoutParams();
- DeviceProfile deviceProfile = mContainer.getDeviceProfile();
- layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
- mTaskView.getFirstSnapshotView().getLayoutParams()).bottomMargin;
- RecentsPagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
- Pair<Float, Float> translations = orientationHandler
- .getDwbLayoutTranslations(mTaskView.getMeasuredWidth(),
- mTaskView.getMeasuredHeight(), mSplitBounds, deviceProfile,
- mTaskView.getSnapshotViews(), mTask.key.id, mBanner);
- mSplitOffsetTranslationX = translations.first;
- mSplitOffsetTranslationY = translations.second;
- updateTranslationY();
- updateTranslationX();
- mTaskView.addView(mBanner);
- }
-
- private void setBannerOutline() {
- // TODO(b\273367585) to investigate why mBanner.getOutlineProvider() can be null
- mOldBannerOutlineProvider = mBanner.getOutlineProvider() != null
- ? mBanner.getOutlineProvider()
- : ViewOutlineProvider.BACKGROUND;
-
- mBanner.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- mOldBannerOutlineProvider.getOutline(view, outline);
- float verticalTranslation = -view.getTranslationY() + mSplitOffsetTranslationY;
- outline.offset(0, Math.round(verticalTranslation));
- }
- });
- mBanner.setClipToOutline(true);
- }
-
- void updateBannerOffset(float offsetPercentage) {
- if (mBannerOffsetPercentage != offsetPercentage) {
- mBannerOffsetPercentage = offsetPercentage;
- if (mBanner != null) {
- updateTranslationY();
- mBanner.invalidateOutline();
- }
- }
- }
-
- private void updateTranslationY() {
- if (mBanner == null) {
- return;
- }
-
- mBanner.setTranslationY(
- (mBannerOffsetPercentage * mBannerHeight) + mSplitOffsetTranslationY);
- }
-
- private void updateTranslationX() {
- if (mBanner == null) {
- return;
- }
-
- mBanner.setTranslationX(mSplitOffsetTranslationX);
- }
-
- void setBannerColorTint(int color, float amount) {
- if (mBanner == null) {
- return;
- }
- if (amount == 0) {
- mBanner.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- Paint layerPaint = new Paint();
- layerPaint.setColorFilter(Utilities.makeColorTintingColorFilter(color, amount));
- mBanner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
- mBanner.setLayerPaint(layerPaint);
- }
-
- void setBannerVisibility(int visibility) {
- if (mBanner == null) {
- return;
- }
-
- mBanner.setVisibility(visibility);
- }
-
- private int getAccessibilityActionId() {
- return (mSplitBounds != null
- && mSplitBounds.rightBottomTaskId == mTask.key.id)
- ? R.id.action_digital_wellbeing_bottom_right
- : R.id.action_digital_wellbeing_top_left;
- }
-
- @Nullable
- public AccessibilityNodeInfo.AccessibilityAction getDWBAccessibilityAction() {
- if (!hasLimit()) {
- return null;
- }
-
- Context context = mContainer.asContext();
- String label =
- (mTaskView.containsMultipleTasks())
- ? context.getString(
- R.string.split_app_usage_settings,
- TaskUtils.getTitle(context, mTask)
- ) : context.getString(R.string.accessibility_app_usage_settings);
- return new AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label);
- }
-
- public boolean handleAccessibilityAction(int action) {
- if (getAccessibilityActionId() == action) {
- openAppUsageSettings(mTaskView);
- return true;
- } else {
- return false;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
new file mode 100644
index 0000000..731b008
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views
+
+import android.app.ActivityOptions
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.pm.LauncherApps
+import android.content.pm.LauncherApps.AppUsageLimit
+import android.graphics.Outline
+import android.graphics.Paint
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
+import android.view.ViewOutlineProvider
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.annotation.StringRes
+import androidx.core.util.component1
+import androidx.core.util.component2
+import androidx.core.view.updateLayoutParams
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.quickstep.TaskUtils
+import com.android.systemui.shared.recents.model.Task
+import java.time.Duration
+import java.util.Locale
+
+class DigitalWellBeingToast(
+ private val container: RecentsViewContainer,
+ private val taskView: TaskView
+) {
+ private val launcherApps: LauncherApps? =
+ container.asContext().getSystemService(LauncherApps::class.java)
+
+ private val bannerHeight =
+ container
+ .asContext()
+ .resources
+ .getDimensionPixelSize(R.dimen.digital_wellbeing_toast_height)
+
+ private lateinit var task: Task
+
+ private var appRemainingTimeMs: Long = 0
+ private var banner: View? = null
+ private var oldBannerOutlineProvider: ViewOutlineProvider? = null
+ private var splitOffsetTranslationY = 0f
+ private var splitOffsetTranslationX = 0f
+
+ private var isDestroyed = false
+
+ var hasLimit = false
+ var splitBounds: SplitConfigurationOptions.SplitBounds? = null
+ var bannerOffsetPercentage = 0f
+ set(value) {
+ if (field != value) {
+ field = value
+ banner?.let {
+ updateTranslationY()
+ it.invalidateOutline()
+ }
+ }
+ }
+
+ private fun setNoLimit() {
+ hasLimit = false
+ taskView.contentDescription = task.titleDescription
+ replaceBanner(null)
+ appRemainingTimeMs = -1
+ }
+
+ private fun setLimit(appUsageLimitTimeMs: Long, appRemainingTimeMs: Long) {
+ this.appRemainingTimeMs = appRemainingTimeMs
+ hasLimit = true
+ val toast =
+ container.viewCache
+ .getView<TextView>(
+ R.layout.digital_wellbeing_toast,
+ container.asContext(),
+ taskView
+ )
+ .apply {
+ text =
+ Utilities.prefixTextWithIcon(
+ container.asContext(),
+ R.drawable.ic_hourglass_top,
+ getBannerText()
+ )
+ setOnClickListener(::openAppUsageSettings)
+ }
+ replaceBanner(toast)
+
+ taskView.contentDescription =
+ getContentDescriptionForTask(task, appUsageLimitTimeMs, appRemainingTimeMs)
+ }
+
+ fun initialize(task: Task) {
+ check(!isDestroyed) { "Cannot re-initialize a destroyed toast" }
+ this.task = task
+ Executors.ORDERED_BG_EXECUTOR.execute {
+ var usageLimit: AppUsageLimit? = null
+ try {
+ usageLimit =
+ launcherApps?.getAppUsageLimit(
+ this.task.topComponent.packageName,
+ UserHandle.of(this.task.key.userId)
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Error initializing digital well being toast", e)
+ }
+ val appUsageLimitTimeMs = usageLimit?.totalUsageLimit ?: -1
+ val appRemainingTimeMs = usageLimit?.usageRemaining ?: -1
+ taskView.post {
+ if (isDestroyed) {
+ return@post
+ }
+ if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
+ setNoLimit()
+ } else {
+ setLimit(appUsageLimitTimeMs, appRemainingTimeMs)
+ }
+ }
+ }
+ }
+
+ /** Mark the DWB toast as destroyed and remove banner from TaskView. */
+ fun destroy() {
+ isDestroyed = true
+ taskView.post { replaceBanner(null) }
+ }
+
+ private fun getSplitBannerConfig(): SplitBannerConfig {
+ val splitBounds = splitBounds
+ return when {
+ splitBounds == null || !container.deviceProfile.isTablet || taskView.isLargeTile ->
+ SplitBannerConfig.SPLIT_BANNER_FULLSCREEN
+ // For portrait grid only height of task changes, not width. So we keep the text the
+ // same
+ !container.deviceProfile.isLeftRightSplit -> SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
+ // For landscape grid, for 30% width we only show icon, otherwise show icon and time
+ task.key.id == splitBounds.leftTopTaskId ->
+ if (splitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY)
+ SplitBannerConfig.SPLIT_GRID_BANNER_SMALL
+ else SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
+ else ->
+ if (splitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY)
+ SplitBannerConfig.SPLIT_GRID_BANNER_SMALL
+ else SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
+ }
+ }
+
+ private fun getReadableDuration(
+ duration: Duration,
+ @StringRes durationLessThanOneMinuteStringId: Int
+ ): String {
+ val hours = Math.toIntExact(duration.toHours())
+ val minutes = Math.toIntExact(duration.minusHours(hours.toLong()).toMinutes())
+ return when {
+ // Apply FormatWidth.WIDE if both the hour part and the minute part are non-zero.
+ hours > 0 && minutes > 0 ->
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.NARROW)
+ .formatMeasures(
+ Measure(hours, MeasureUnit.HOUR),
+ Measure(minutes, MeasureUnit.MINUTE)
+ )
+ // Apply FormatWidth.WIDE if only the hour part is non-zero (unless forced).
+ hours > 0 ->
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(Measure(hours, MeasureUnit.HOUR))
+ // Apply FormatWidth.WIDE if only the minute part is non-zero (unless forced).
+ minutes > 0 ->
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(Measure(minutes, MeasureUnit.MINUTE))
+ // Use a specific string for usage less than one minute but non-zero.
+ duration > Duration.ZERO ->
+ container.asContext().getString(durationLessThanOneMinuteStringId)
+ // Otherwise, return 0-minute string.
+ else ->
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(Measure(0, MeasureUnit.MINUTE))
+ }
+ }
+
+ /**
+ * Returns text to show for the banner depending on [.getSplitBannerConfig] If {@param
+ * forContentDesc} is `true`, this will always return the full string corresponding to
+ * [.SPLIT_BANNER_FULLSCREEN]
+ */
+ @JvmOverloads
+ fun getBannerText(
+ remainingTime: Long = appRemainingTimeMs,
+ forContentDesc: Boolean = false
+ ): String {
+ val duration =
+ Duration.ofMillis(
+ if (remainingTime > MINUTE_MS)
+ (remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS
+ else remainingTime
+ )
+ val readableDuration =
+ getReadableDuration(
+ duration,
+ R.string.shorter_duration_less_than_one_minute /* forceFormatWidth */
+ )
+ val splitBannerConfig = getSplitBannerConfig()
+ return when {
+ forContentDesc || splitBannerConfig == SplitBannerConfig.SPLIT_BANNER_FULLSCREEN ->
+ container.asContext().getString(R.string.time_left_for_app, readableDuration)
+ // show no text
+ splitBannerConfig == SplitBannerConfig.SPLIT_GRID_BANNER_SMALL -> ""
+ // SPLIT_GRID_BANNER_LARGE only show time
+ else -> readableDuration
+ }
+ }
+
+ private fun openAppUsageSettings(view: View) {
+ val intent =
+ Intent(OPEN_APP_USAGE_SETTINGS_TEMPLATE)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, task.topComponent.packageName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ try {
+ val options = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.width, view.height)
+ container.asContext().startActivity(intent, options.toBundle())
+
+ // TODO: add WW logging on the app usage settings click.
+ } catch (e: ActivityNotFoundException) {
+ Log.e(
+ TAG,
+ "Failed to open app usage settings for task " + task.topComponent.packageName,
+ e
+ )
+ }
+ }
+
+ private fun getContentDescriptionForTask(
+ task: Task,
+ appUsageLimitTimeMs: Long,
+ appRemainingTimeMs: Long
+ ): String =
+ if (appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0)
+ container
+ .asContext()
+ .getString(
+ R.string.task_contents_description_with_remaining_time,
+ task.titleDescription,
+ getBannerText(appRemainingTimeMs, true /* forContentDesc */)
+ )
+ else task.titleDescription
+
+ private fun replaceBanner(view: View?) {
+ resetOldBanner()
+ setBanner(view)
+ }
+
+ private fun resetOldBanner() {
+ val banner = banner ?: return
+ banner.outlineProvider = oldBannerOutlineProvider
+ taskView.removeView(banner)
+ banner.setOnClickListener(null)
+ container.viewCache.recycleView(R.layout.digital_wellbeing_toast, banner)
+ }
+
+ private fun setBanner(banner: View?) {
+ this.banner = banner
+ if (banner != null && taskView.recentsView != null) {
+ setupAndAddBanner()
+ setBannerOutline()
+ }
+ }
+
+ private fun setupAndAddBanner() {
+ val banner = banner ?: return
+ banner.updateLayoutParams<FrameLayout.LayoutParams> {
+ bottomMargin =
+ (taskView.firstSnapshotView.layoutParams as MarginLayoutParams).bottomMargin
+ }
+ val (translationX, translationY) =
+ taskView.pagedOrientationHandler.getDwbLayoutTranslations(
+ taskView.measuredWidth,
+ taskView.measuredHeight,
+ splitBounds,
+ container.deviceProfile,
+ taskView.snapshotViews,
+ task.key.id,
+ banner
+ )
+ splitOffsetTranslationX = translationX
+ splitOffsetTranslationY = translationY
+ updateTranslationY()
+ updateTranslationX()
+ taskView.addView(banner)
+ }
+
+ private fun setBannerOutline() {
+ val banner = banner ?: return
+ // TODO(b\273367585) to investigate why mBanner.getOutlineProvider() can be null
+ val oldBannerOutlineProvider =
+ if (banner.outlineProvider != null) banner.outlineProvider
+ else ViewOutlineProvider.BACKGROUND
+ this.oldBannerOutlineProvider = oldBannerOutlineProvider
+
+ banner.outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ oldBannerOutlineProvider.getOutline(view, outline)
+ val verticalTranslation = -view.translationY + splitOffsetTranslationY
+ outline.offset(0, Math.round(verticalTranslation))
+ }
+ }
+ banner.clipToOutline = true
+ }
+
+ private fun updateTranslationY() {
+ banner?.translationY = bannerOffsetPercentage * bannerHeight + splitOffsetTranslationY
+ }
+
+ private fun updateTranslationX() {
+ banner?.translationX = splitOffsetTranslationX
+ }
+
+ fun setBannerColorTint(color: Int, amount: Float) {
+ val banner = banner ?: return
+ if (amount == 0f) {
+ banner.setLayerType(View.LAYER_TYPE_NONE, null)
+ }
+ val layerPaint = Paint()
+ layerPaint.setColorFilter(Utilities.makeColorTintingColorFilter(color, amount))
+ banner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint)
+ banner.setLayerPaint(layerPaint)
+ }
+
+ fun setBannerVisibility(visibility: Int) {
+ banner?.visibility = visibility
+ }
+
+ private fun getAccessibilityActionId(): Int =
+ if (splitBounds?.rightBottomTaskId == task.key.id)
+ R.id.action_digital_wellbeing_bottom_right
+ else R.id.action_digital_wellbeing_top_left
+
+ fun getDWBAccessibilityAction(): AccessibilityNodeInfo.AccessibilityAction? {
+ if (!hasLimit) return null
+ val context = container.asContext()
+ val label =
+ if ((taskView.containsMultipleTasks()))
+ context.getString(
+ R.string.split_app_usage_settings,
+ TaskUtils.getTitle(context, task)
+ )
+ else context.getString(R.string.accessibility_app_usage_settings)
+ return AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label)
+ }
+
+ fun handleAccessibilityAction(action: Int): Boolean {
+ if (getAccessibilityActionId() != action) return false
+ openAppUsageSettings(taskView)
+ return true
+ }
+
+ companion object {
+ private const val THRESHOLD_LEFT_ICON_ONLY = 0.4f
+ private const val THRESHOLD_RIGHT_ICON_ONLY = 0.6f
+
+ enum class SplitBannerConfig {
+ /** Will span entire width of taskView with full text */
+ SPLIT_BANNER_FULLSCREEN,
+ /** Used for grid task view, only showing icon and time */
+ SPLIT_GRID_BANNER_LARGE,
+ /** Used for grid task view, only showing icon */
+ SPLIT_GRID_BANNER_SMALL
+ }
+
+ val OPEN_APP_USAGE_SETTINGS_TEMPLATE: Intent = Intent(Settings.ACTION_APP_USAGE_SETTINGS)
+ const val MINUTE_MS: Int = 60000
+
+ private const val TAG = "DigitalWellBeingToast"
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/FixedSizeImageView.kt b/quickstep/src/com/android/quickstep/views/FixedSizeImageView.kt
new file mode 100644
index 0000000..c893016
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FixedSizeImageView.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.views
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ViewGroup
+import android.widget.ImageView
+
+/**
+ * An [ImageView] that does not requestLayout() unless setLayoutParams is called.
+ *
+ * This is useful, particularly during animations, for [ImageView]s that are not supposed to be
+ * resized.
+ */
+@SuppressLint("AppCompatCustomView")
+class FixedSizeImageView : ImageView {
+ private var shouldRequestLayoutOnChanges = false
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ override fun setLayoutParams(params: ViewGroup.LayoutParams?) {
+ shouldRequestLayoutOnChanges = true
+ super.setLayoutParams(params)
+ shouldRequestLayoutOnChanges = false
+ }
+
+ override fun requestLayout() {
+ if (shouldRequestLayoutOnChanges) {
+ super.requestLayout()
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index a046c42..ba4d786 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -130,7 +130,7 @@
taskContainers.forEach { it.bind() }
this.splitBoundsConfig = splitBoundsConfig
- taskContainers.forEach { it.digitalWellBeingToast?.setSplitBounds(splitBoundsConfig) }
+ taskContainers.forEach { it.digitalWellBeingToast?.splitBounds = splitBoundsConfig }
setOrientationState(orientedState)
}
@@ -210,7 +210,7 @@
fun updateSplitBoundsConfig(splitBounds: SplitConfigurationOptions.SplitBounds?) {
splitBoundsConfig = splitBounds
taskContainers.forEach {
- it.digitalWellBeingToast?.setSplitBounds(splitBoundsConfig)
+ it.digitalWellBeingToast?.splitBounds = splitBoundsConfig
it.digitalWellBeingToast?.initialize(it.task)
}
invalidate()
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index d9468c7..4a2be2a 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -274,7 +274,9 @@
}
private void updateActionButtonsVisibility() {
- assert mDp != null;
+ if (mDp == null) {
+ return;
+ }
boolean showSingleTaskActions = !mIsGroupedTask;
boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
Log.d(TAG, "updateActionButtonsVisibility() called: showSingleTaskActions = ["
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index bb2a12f..255619a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -36,7 +36,9 @@
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
+import static com.android.launcher3.Flags.enableDesktopTaskAlphaAnimation;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
@@ -211,6 +213,7 @@
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.RecentsViewUtils;
import com.android.quickstep.util.SplitAnimationController.Companion.SplitAnimInitProps;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.SplitSelectStateController;
@@ -229,9 +232,9 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import kotlin.Unit;
@@ -254,8 +257,8 @@
* @param <CONTAINER_TYPE> : the container that should host recents view
* @param <STATE_TYPE> : the type of base state that will be used
*/
-
-public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewContainer,
+public abstract class RecentsView<
+ CONTAINER_TYPE extends Context & RecentsViewContainer,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener {
@@ -318,6 +321,27 @@
}
};
+ public static final FloatProperty<RecentsView> RUNNING_TASK_ATTACH_ALPHA =
+ new FloatProperty<RecentsView>("runningTaskAttachAlpha") {
+ @Override
+ public void setValue(RecentsView recentsView, float v) {
+ TaskView runningTask = recentsView.getRunningTaskView();
+ if (runningTask == null) {
+ return;
+ }
+ runningTask.setAttachAlpha(v);
+ }
+
+ @Override
+ public Float get(RecentsView recentsView) {
+ TaskView runningTask = recentsView.getRunningTaskView();
+ if (runningTask == null) {
+ return null;
+ }
+ return runningTask.getAttachAlpha();
+ }
+ };
+
public static final int SCROLL_VIBRATION_PRIMITIVE =
Utilities.ATLEAST_S ? VibrationEffect.Composition.PRIMITIVE_LOW_TICK : -1;
public static final float SCROLL_VIBRATION_PRIMITIVE_SCALE = 0.6f;
@@ -656,7 +680,7 @@
protected boolean mRunningTaskTileHidden;
@Nullable
private Task[] mTmpRunningTasks;
- protected int mFocusedTaskViewId = -1;
+ protected int mFocusedTaskViewId = INVALID_TASK_ID;
private boolean mTaskIconScaledDown = false;
private boolean mRunningTaskShowScreenshot = false;
@@ -813,7 +837,9 @@
private boolean mAnyTaskHasBeenDismissed;
private final RecentsViewModel mRecentsViewModel;
- private final RecentsViewHelper mHelper;
+ private final RecentsViewModelHelper mHelper;
+
+ private final RecentsViewUtils mRecentsViewUtils = new RecentsViewUtils();
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
BaseContainerInterface sizeStrategy) {
@@ -835,7 +861,7 @@
recentsDependencies.inject(RecentTasksRepository.class),
recentsDependencies.inject(RecentsViewData.class)
);
- mHelper = new RecentsViewHelper(mRecentsViewModel);
+ mHelper = new RecentsViewModelHelper(mRecentsViewModel);
recentsDependencies.provide(RecentsRotationStateRepository.class,
() -> new RecentsRotationStateRepositoryImpl(mOrientationState));
@@ -1053,7 +1079,6 @@
@Nullable
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
if (enableRefactorTaskThumbnail()) {
- mHelper.onTaskThumbnailChanged(taskId, thumbnailData);
return null;
}
if (mHandleTaskStackChanges) {
@@ -1074,8 +1099,7 @@
}
@Override
- public void onTaskIconChanged(String pkg, UserHandle user) {
- // TODO(b/342560598): Listen in TaskRepository and reload.
+ public void onTaskIconChanged(@NonNull String pkg, @NonNull UserHandle user) {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = requireTaskViewAt(i);
Task task = tv.getFirstTask();
@@ -1685,10 +1709,10 @@
}
TaskView taskView = getTaskViewAt(mNextPage);
// Snap to fully visible focused task and clear all button.
- boolean shouldSnapToFocusedTask = taskView != null && taskView.isFocusedTask()
+ boolean shouldSnapToLargeTask = taskView != null && taskView.isLargeTile()
&& isTaskViewFullyVisible(taskView);
boolean shouldSnapToClearAll = mNextPage == indexOfChild(mClearAllButton);
- if (!shouldSnapToFocusedTask && !shouldSnapToClearAll) {
+ if (!shouldSnapToLargeTask && !shouldSnapToClearAll) {
return;
}
}
@@ -1748,7 +1772,9 @@
return;
}
- if (mCurrentPage == 0) {
+ int frontIndex = enableLargeDesktopWindowingTile() ? getDesktopTaskViewCount() : 0;
+
+ if (mCurrentPage <= frontIndex) {
return;
}
@@ -1760,8 +1786,9 @@
removeView(runningTaskView);
mMovingTaskView = null;
runningTaskView.resetPersistentViewTransforms();
- addView(runningTaskView, 0);
- setCurrentPage(0);
+
+ addView(runningTaskView, frontIndex);
+ setCurrentPage(frontIndex);
updateTaskSize();
}
@@ -1779,7 +1806,8 @@
protected void applyLoadPlan(List<GroupTask> taskGroups) {
if (mPendingAnimation != null) {
- mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
+ final List<GroupTask> finalTaskGroups = taskGroups;
+ mPendingAnimation.addEndListener(success -> applyLoadPlan(finalTaskGroups));
return;
}
@@ -1824,12 +1852,15 @@
// Reset the focused task to avoiding initializing TaskViews layout as focused task during
// binding. The focused task view will be updated after all the TaskViews are bound.
- mFocusedTaskViewId = INVALID_TASK_ID;
+ setFocusedTaskViewId(INVALID_TASK_ID);
// Removing views sets the currentPage to 0, so we save this and restore it after
// the new set of views are added
int previousCurrentPage = mCurrentPage;
int previousFocusedPage = indexOfChild(getFocusedChild());
+ // TaskIds will no longer be valid after remove and re-add, clearing mTopRowIdSet.
+ mAnyTaskHasBeenDismissed = false;
+ mTopRowIdSet.clear();
removeAllViews();
// If we are entering Overview as a result of initiating a split from somewhere else
@@ -1847,6 +1878,11 @@
// Clear out desktop view if it is set
mDesktopTaskView = null;
+ // Move Desktop Tasks to the end of the list
+ if (enableLargeDesktopWindowingTile()) {
+ taskGroups = mRecentsViewUtils.sortDesktopTasksToFront(taskGroups);
+ }
+
// Add views as children based on whether it's grouped or single task. Looping through
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
@@ -1900,11 +1936,14 @@
// Keep same previous focused task
TaskView newFocusedTaskView = getTaskViewByTaskIds(focusedTaskIds);
// If the list changed, maybe the focused task doesn't exist anymore
- if (newFocusedTaskView == null && getTaskViewCount() > 0) {
- newFocusedTaskView = getTaskViewAt(0);
+ int newFocusedTaskViewIndex = mRecentsViewUtils.getFocusedTaskIndex(taskGroups);
+ if (newFocusedTaskView == null && getTaskViewCount() > newFocusedTaskViewIndex) {
+ newFocusedTaskView = getTaskViewAt(newFocusedTaskViewIndex);
}
- mFocusedTaskViewId = newFocusedTaskView != null && !enableGridOnlyOverview()
- ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID;
+
+ setFocusedTaskViewId(newFocusedTaskView != null && !enableGridOnlyOverview()
+ ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID);
+
updateTaskSize();
updateChildTaskOrientations();
@@ -1944,8 +1983,8 @@
// Set the current page to the running task, but not if settling on new task.
if (hasAllValidTaskIds(runningTaskIds)) {
targetPage = indexOfChild(newRunningTaskView);
- } else if (getTaskViewCount() > 0) {
- targetPage = indexOfChild(requireTaskViewAt(0));
+ } else if (getTaskViewCount() > newFocusedTaskViewIndex) {
+ targetPage = indexOfChild(requireTaskViewAt(newFocusedTaskViewIndex));
}
}
if (targetPage != -1 && mCurrentPage != targetPage) {
@@ -1970,6 +2009,7 @@
// generally map to the same task.
mIgnoreResetTaskId = INVALID_TASK_ID;
}
+
resetTaskVisuals();
onTaskStackUpdated();
updateEnabledOverlays();
@@ -2010,14 +2050,13 @@
return taskViewCount;
}
- public int getGroupedTaskViewCount() {
- int groupViewCount = 0;
- for (int i = 0; i < getChildCount(); i++) {
- if (getChildAt(i) instanceof GroupedTaskView) {
- groupViewCount++;
- }
- }
- return groupViewCount;
+ /**
+ * Transverse RecentsView children to calculate the amount of DesktopTaskViews.
+ *
+ * @return Number of children that are instances of DesktopTaskView
+ */
+ private int getDesktopTaskViewCount() {
+ return mRecentsViewUtils.getDesktopTaskViewCount(getChildCount(), this::getTaskViewAt);
}
/**
@@ -2508,7 +2547,6 @@
}
if (enableRefactorTaskThumbnail()) {
- // TODO(b/342560598): Listen in TaskRepository and reload.
return;
}
@@ -2549,7 +2587,7 @@
mCurrentPageScrollDiff = 0;
mIgnoreResetTaskId = -1;
mTaskListChangeId = -1;
- mFocusedTaskViewId = -1;
+ setFocusedTaskViewId(INVALID_TASK_ID);
mAnyTaskHasBeenDismissed = false;
@@ -2621,6 +2659,10 @@
return getTaskViewFromTaskViewId(mFocusedTaskViewId);
}
+ private @Nullable TaskView getFirstLargeTaskView() {
+ return mRecentsViewUtils.getFirstLargeTaskView(getChildCount(), this::getTaskViewAt);
+ }
+
@Nullable
private TaskView getTaskViewFromTaskViewId(int taskViewId) {
if (taskViewId == -1) {
@@ -2714,10 +2756,17 @@
showCurrentTask(mActiveGestureRunningTasks);
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
- setRunningTaskHidden(true);
+ setRunningTaskHidden(!shouldUpdateRunningTaskAlpha());
setTaskIconScaledDown(true);
}
+ /**
+ * Returns whether the running task's attach alpha should be updated during the attach animation
+ */
+ public boolean shouldUpdateRunningTaskAlpha() {
+ return enableDesktopTaskAlphaAnimation() && getRunningTaskView() instanceof DesktopTaskView;
+ }
+
private boolean isGestureActive() {
return mActiveGestureRunningTasks != null;
}
@@ -2882,6 +2931,7 @@
if (runningTasks.length == 0) {
return;
}
+
int runningTaskViewId = -1;
boolean needGroupTaskView = runningTasks.length > 1;
boolean needDesktopTask = hasDesktopTask(runningTasks);
@@ -2926,7 +2976,11 @@
boolean runningTaskTileHidden = mRunningTaskTileHidden;
setCurrentTask(runningTaskViewId);
- mFocusedTaskViewId = enableGridOnlyOverview() ? INVALID_TASK_ID : runningTaskViewId;
+
+ boolean shouldFocusRunningTask = !(enableGridOnlyOverview()
+ || (enableLargeDesktopWindowingTile()
+ && getRunningTaskView() instanceof DesktopTaskView));
+ setFocusedTaskViewId(shouldFocusRunningTask ? runningTaskViewId : INVALID_TASK_ID);
runOnPageScrollsInitialized(() -> setCurrentPage(getRunningTaskIndex()));
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
@@ -2978,6 +3032,10 @@
}
}
+ private void setFocusedTaskViewId(int viewId) {
+ mFocusedTaskViewId = viewId;
+ }
+
private int getTaskViewIdFromTaskId(int taskId) {
TaskView taskView = getTaskViewByTaskId(taskId);
return taskView != null ? taskView.getTaskViewId() : -1;
@@ -2989,12 +3047,13 @@
public void setRunningTaskHidden(boolean isHidden) {
mRunningTaskTileHidden = isHidden;
TaskView runningTask = getRunningTaskView();
- if (runningTask != null) {
- runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
- if (!isHidden) {
- AccessibilityManagerCompat.sendCustomAccessibilityEvent(runningTask,
- AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
- }
+ if (runningTask == null) {
+ return;
+ }
+ runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
+ if (!isHidden) {
+ AccessibilityManagerCompat.sendCustomAccessibilityEvent(
+ runningTask, AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
}
@@ -3003,6 +3062,10 @@
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot);
+ if (!enableRefactorTaskThumbnail()) {
+ runningTaskView.getTaskContainers().forEach(
+ taskContainer -> taskContainer.getThumbnailViewDeprecated().refresh());
+ }
}
if (enableRefactorTaskThumbnail()) {
mRecentsViewModel.setRunningTaskShowScreenshot(showScreenshot);
@@ -3077,8 +3140,9 @@
float[] gridTranslations = new float[taskCount];
int focusedTaskIndex = Integer.MAX_VALUE;
+ Set<Integer> largeTasksIndices = new HashSet<>();
int focusedTaskShift = 0;
- int focusedTaskWidthAndSpacing = 0;
+ int largeTaskWidthAndSpacing = 0;
int snappedTaskRowWidth = 0;
int snappedPage = isKeyboardTaskFocusPending() ? mKeyboardTaskFocusIndex : getNextPage();
TaskView snappedTaskView = getTaskViewAt(snappedPage);
@@ -3095,12 +3159,11 @@
// Evenly distribute tasks between rows unless rearranging due to task dismissal, in
// which case keep tasks in their respective rows. For the running task, don't join
// the grid.
- if (taskView.isFocusedTask()) {
+ boolean isLargeTile = taskView.isLargeTile();
+
+ if (isLargeTile) {
topRowWidth += taskWidthAndSpacing;
bottomRowWidth += taskWidthAndSpacing;
-
- focusedTaskIndex = i;
- focusedTaskWidthAndSpacing = taskWidthAndSpacing;
gridTranslations[i] += focusedTaskShift;
gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
@@ -3108,6 +3171,12 @@
taskView.setGridTranslationY((mLastComputedTaskSize.height() + taskTopMargin
- taskView.getLayoutParams().height) / 2f);
+ if (taskView.getTaskViewId() == mFocusedTaskViewId) {
+ focusedTaskIndex = i;
+ }
+ largeTasksIndices.add(i);
+ largeTaskWidthAndSpacing = taskWidthAndSpacing;
+
if (taskView == snappedTaskView) {
// If focused task is snapped, the row width is just task width and spacing.
snappedTaskRowWidth = taskWidthAndSpacing;
@@ -3116,7 +3185,7 @@
if (i > focusedTaskIndex) {
// For tasks after the focused task, shift by focused task's width and spacing.
gridTranslations[i] +=
- mIsRtl ? focusedTaskWidthAndSpacing : -focusedTaskWidthAndSpacing;
+ mIsRtl ? largeTaskWidthAndSpacing : -largeTaskWidthAndSpacing;
} else {
// For task before the focused task, accumulate the width and spacing to
// calculate the distance focused task need to shift.
@@ -3152,7 +3221,7 @@
// Move horizontally into empty space.
float widthOffset = 0;
for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) {
- if (j == focusedTaskIndex) {
+ if (largeTasksIndices.contains(j)) {
continue;
}
widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
@@ -3171,7 +3240,7 @@
// Move horizontally into empty space.
float widthOffset = 0;
for (int j = i - 1; !bottomSet.contains(j) && j >= 0; j--) {
- if (j == focusedTaskIndex) {
+ if (largeTasksIndices.contains(j)) {
continue;
}
widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
@@ -3221,12 +3290,22 @@
// accordingly. Update longRowWidth if ClearAllButton has been moved.
float clearAllShortTotalWidthTranslation = 0;
int longRowWidth = Math.max(topRowWidth, bottomRowWidth);
- if (longRowWidth < mLastComputedGridSize.width()) {
- mClearAllShortTotalWidthTranslation =
- (mIsRtl
- ? mLastComputedTaskSize.right
- : deviceProfile.widthPx - mLastComputedTaskSize.left)
- - longRowWidth - deviceProfile.overviewGridSideMargin;
+
+ // If Recents contains only large task sizes, it should only consider 1 large size
+ // for ClearAllButton translation. The space at the left side of the large task will be
+ // empty and it should be move ClearAllButton further away as well.
+ // TODO(b/359573248): Validate the translation for ClearAllButton for grid only.
+ boolean hasOnlyLargeTasks = taskCount == largeTasksIndices.size();
+ if (enableLargeDesktopWindowingTile() && hasOnlyLargeTasks) {
+ longRowWidth = largeTaskWidthAndSpacing;
+ }
+
+ // If first task is not in the expected position (mLastComputedTaskSize) and being too close
+ // to ClearAllButton, then apply extra translation to ClearAllButton.
+ int firstTaskStart = mLastComputedGridSize.left + longRowWidth;
+ int expectedFirstTaskStart = mLastComputedTaskSize.right;
+ if (firstTaskStart < expectedFirstTaskStart) {
+ mClearAllShortTotalWidthTranslation = expectedFirstTaskStart - firstTaskStart;
clearAllShortTotalWidthTranslation = mIsRtl
? -mClearAllShortTotalWidthTranslation : mClearAllShortTotalWidthTranslation;
if (snappedTaskRowWidth == longRowWidth) {
@@ -3241,10 +3320,10 @@
float clearAllTotalTranslationX =
clearAllAccumulatedTranslation + clearAllShorterRowCompensation
+ clearAllShortTotalWidthTranslation + snappedTaskNonGridScrollAdjustment;
- if (focusedTaskIndex < taskCount) {
+ if (!largeTasksIndices.isEmpty()) {
// Shift by focused task's width and spacing if a task is focused.
clearAllTotalTranslationX +=
- mIsRtl ? focusedTaskWidthAndSpacing : -focusedTaskWidthAndSpacing;
+ mIsRtl ? largeTaskWidthAndSpacing : -largeTaskWidthAndSpacing;
}
// Make sure there are enough space between snapped page and ClearAllButton, for the case
@@ -3284,7 +3363,6 @@
mClearAllButton.setGridScrollOffset(
mIsRtl ? mLastComputedTaskSize.left - mLastComputedGridSize.left
: mLastComputedTaskSize.right - mLastComputedGridSize.right);
-
setGridProgress(mGridProgress);
}
@@ -3292,11 +3370,11 @@
if (taskView1 == null || taskView2 == null) {
return false;
}
- int taskViewId1 = taskView1.getTaskViewId();
- int taskViewId2 = taskView2.getTaskViewId();
- if (taskViewId1 == mFocusedTaskViewId || taskViewId2 == mFocusedTaskViewId) {
+ if (taskView1.isLargeTile() || taskView2.isLargeTile()) {
return false;
}
+ int taskViewId1 = taskView1.getTaskViewId();
+ int taskViewId2 = taskView2.getTaskViewId();
return (mTopRowIdSet.contains(taskViewId1) && mTopRowIdSet.contains(taskViewId2)) || (
!mTopRowIdSet.contains(taskViewId1) && !mTopRowIdSet.contains(taskViewId2));
}
@@ -3549,11 +3627,11 @@
isStagingFocusedTask = true;
} else {
nextFocusedTaskFromTop =
- mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
+ !mTopRowIdSet.isEmpty() && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
// Pick the next focused task from the preferred row.
for (int i = 0; i < taskCount; i++) {
TaskView taskView = requireTaskViewAt(i);
- if (taskView == dismissedTaskView) {
+ if (taskView == dismissedTaskView || taskView.isLargeTile()) {
continue;
}
boolean isTopRow = mTopRowIdSet.contains(taskView.getTaskViewId());
@@ -4017,9 +4095,9 @@
} else {
// Update focus task and its size.
if (finalIsFocusedTaskDismissed && finalNextFocusedTaskView != null) {
- mFocusedTaskViewId = enableGridOnlyOverview()
+ setFocusedTaskViewId(enableGridOnlyOverview()
? INVALID_TASK_ID
- : finalNextFocusedTaskView.getTaskViewId();
+ : finalNextFocusedTaskView.getTaskViewId());
mTopRowIdSet.remove(mFocusedTaskViewId);
finalNextFocusedTaskView.animateIconScaleAndDimIntoView();
}
@@ -4162,8 +4240,9 @@
IntArray bottomArray = new IntArray(bottomRowIdArraySize);
int taskViewCount = getTaskViewCount();
for (int i = 0; i < taskViewCount; i++) {
- int taskViewId = requireTaskViewAt(i).getTaskViewId();
- if (!mTopRowIdSet.contains(taskViewId) && taskViewId != mFocusedTaskViewId) {
+ TaskView taskView = requireTaskViewAt(i);
+ int taskViewId = taskView.getTaskViewId();
+ if (!mTopRowIdSet.contains(taskViewId) && !taskView.isLargeTile()) {
bottomArray.add(taskViewId);
}
}
@@ -4269,6 +4348,7 @@
}
// Init task grid nav helper with top/bottom id arrays.
+ // TODO(b/361070854): Add keyboard navigation for all large tiles.
TaskGridNavHelper taskGridNavHelper = new TaskGridNavHelper(getTopRowIdArray(),
getBottomRowIdArray(), mFocusedTaskViewId);
@@ -4597,9 +4677,10 @@
? (runningTask == null ? INVALID_PAGE : indexOfChild(runningTask))
: mOffsetMidpointIndexOverride;
int modalMidpoint = getCurrentPage();
- boolean isModalGridWithoutFocusedTask =
- showAsGrid && enableGridOnlyOverview() && mTaskModalness > 0;
- if (isModalGridWithoutFocusedTask) {
+ boolean shouldCalculateOffsetForAllTasks = showAsGrid
+ && (enableGridOnlyOverview() || enableLargeDesktopWindowingTile())
+ && mTaskModalness > 0;
+ if (shouldCalculateOffsetForAllTasks) {
modalMidpoint = indexOfChild(mSelectedTask);
}
@@ -4638,7 +4719,7 @@
: i < midpoint
? leftOffsetSize
: rightOffsetSize;
- if (isModalGridWithoutFocusedTask) {
+ if (shouldCalculateOffsetForAllTasks) {
gridOffsetSize = getHorizontalOffsetSize(i, modalMidpoint, modalOffset);
gridOffsetSize = Math.abs(gridOffsetSize) * (i <= modalMidpoint ? 1 : -1);
}
@@ -4647,8 +4728,11 @@
: showAsGrid
? gridOffsetSize
: i < modalMidpoint ? modalLeftOffsetSize : modalRightOffsetSize;
- float totalTranslationX = translation + modalTranslation;
View child = getChildAt(i);
+ boolean skipTranslationOffset = enableDesktopTaskAlphaAnimation()
+ && i == getRunningTaskIndex()
+ && child instanceof DesktopTaskView;
+ float totalTranslationX = (skipTranslationOffset ? 0f : translation) + modalTranslation;
FloatProperty translationPropertyX = child instanceof TaskView
? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
: getPagedOrientationHandler().getPrimaryViewTranslate();
@@ -5244,10 +5328,7 @@
float toScale = getMaxScaleForFullScreen();
boolean showAsGrid = showAsGrid();
- boolean zoomInTaskView = showAsGrid
- ? ((taskView.isFocusedTask() && isTaskViewFullyVisible(taskView))
- || taskView instanceof DesktopTaskView)
- : taskIndex == centerTaskIndex;
+ boolean zoomInTaskView = showAsGrid ? taskView.isLargeTile() : taskIndex == centerTaskIndex;
if (zoomInTaskView) {
anim.play(ObjectAnimator.ofFloat(this, RECENTS_SCALE_PROPERTY, toScale));
anim.play(ObjectAnimator.ofFloat(this, FULLSCREEN_PROGRESS, 1));
@@ -5728,8 +5809,8 @@
}
private int getFirstViewIndex() {
- TaskView focusedTaskView = mShowAsGridLastOnLayout ? getFocusedTaskView() : null;
- return focusedTaskView != null ? indexOfChild(focusedTaskView) : 0;
+ TaskView firstTaskView = mShowAsGridLastOnLayout ? getFirstLargeTaskView() : null;
+ return firstTaskView != null ? indexOfChild(firstTaskView) : 0;
}
private int getLastViewIndex() {
@@ -5747,7 +5828,7 @@
}
// Returns focus task if there are no grid tasks.
- return indexOfChild(getFocusedTaskView());
+ return indexOfChild(getFirstLargeTaskView());
}
/**
@@ -5963,7 +6044,7 @@
public boolean isOnGridBottomRow(TaskView taskView) {
return showAsGrid()
&& !mTopRowIdSet.contains(taskView.getTaskViewId())
- && taskView.getTaskViewId() != mFocusedTaskViewId;
+ && !taskView.isLargeTile();
}
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
similarity index 86%
rename from quickstep/src/com/android/quickstep/views/RecentsViewHelper.kt
rename to quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
index e8c9718..f5b2176 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewHelper.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewModelHelper.kt
@@ -27,8 +27,8 @@
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
-/** Helper for [RecentsView] to interact with coroutine. */
-class RecentsViewHelper(private val recentsViewModel: RecentsViewModel) {
+/** Helper for [RecentsView] to interact with the [RecentsViewModel]. */
+class RecentsViewModelHelper(private val recentsViewModel: RecentsViewModel) {
private lateinit var viewAttachedScope: CoroutineScope
fun onAttachedToWindow() {
@@ -43,7 +43,7 @@
fun switchToScreenshot(
taskView: TaskView,
recentsAnimationController: RecentsAnimationController,
- onFinishRunnable: Runnable
+ onFinishRunnable: Runnable,
) {
val updatedThumbnails =
taskView.taskContainers.associate {
@@ -55,7 +55,7 @@
fun switchToScreenshot(
taskView: TaskView,
updatedThumbnails: Map<Int, ThumbnailData>?,
- onFinishRunnable: Runnable
+ onFinishRunnable: Runnable,
) {
// Update recentsViewModel and apply the thumbnailOverride ASAP, before waiting inside
// viewAttachedScope.
@@ -68,8 +68,4 @@
ViewUtils.postFrameDrawn(taskView, onFinishRunnable)
}
}
-
- fun onTaskThumbnailChanged(taskId: Int, thumbnailData: ThumbnailData) {
- recentsViewModel.addOrUpdateThumbnailOverride(mapOf(taskId to thumbnailData))
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 158ae33..19d706f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -99,7 +99,7 @@
private var optionMeasuredHeight = 0
private val arrowHorizontalPadding: Int
get() =
- if (taskView.isFocusedTask)
+ if (taskView.isLargeTile)
resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
else 0
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 176a538..815f8fa 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -49,6 +49,7 @@
import com.android.launcher3.Flags.enableFocusOutline
import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.Flags.enableHoverOfChildElementsInTaskview
+import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
@@ -64,6 +65,7 @@
import com.android.launcher3.util.Executors
import com.android.launcher3.util.MultiPropertyFactory
import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
+import com.android.launcher3.util.MultiValueAlpha
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SafeCloseable
import com.android.launcher3.util.SplitConfigurationOptions
@@ -108,7 +110,7 @@
defStyleRes: Int = 0,
focusBorderAnimator: BorderAnimator? = null,
hoverBorderAnimator: BorderAnimator? = null,
- type: TaskViewType = TaskViewType.SINGLE
+ private val type: TaskViewType = TaskViewType.SINGLE,
) : FrameLayout(context, attrs), ViewPool.Reusable {
/**
* Used in conjunction with [onTaskListVisibilityChanged], providing more granularity on which
@@ -133,13 +135,15 @@
val isGridTask: Boolean
/** Returns whether the task is part of overview grid and not being focused. */
- get() = container.deviceProfile.isTablet && !isFocusedTask
+ get() = container.deviceProfile.isTablet && !isLargeTile
val isRunningTask: Boolean
get() = this === recentsView?.runningTaskView
- val isFocusedTask: Boolean
- get() = this === recentsView?.focusedTaskView
+ val isLargeTile: Boolean
+ get() =
+ this == recentsView?.focusedTaskView ||
+ (enableLargeDesktopWindowingTile() && type == TaskViewType.DESKTOP)
val taskCornerRadius: Float
get() = currentFullscreenParams.cornerRadius
@@ -290,9 +294,6 @@
set(value) {
field = value
applyScale()
- if (enableRefactorTaskThumbnail()) {
- taskViewModel.updateNonGridScale(value)
- }
}
private var dismissScale = 1f
@@ -391,11 +392,19 @@
applyTranslationX()
}
- protected var stableAlpha = 1f
+ private val taskViewAlpha = MultiValueAlpha(this, NUM_ALPHA_CHANNELS)
+
+ protected var stableAlpha
set(value) {
- field = value
- alpha = stableAlpha
+ taskViewAlpha.get(ALPHA_INDEX_STABLE).value = value
}
+ get() = taskViewAlpha.get(ALPHA_INDEX_STABLE).value
+
+ protected var attachAlpha
+ set(value) {
+ taskViewAlpha.get(ALPHA_INDEX_ATTACH).value = value
+ }
+ get() = taskViewAlpha.get(ALPHA_INDEX_ATTACH).value
protected var shouldShowScreenshot = false
get() = !isRunningTask || field
@@ -521,7 +530,7 @@
public override fun onFocusChanged(
gainFocus: Boolean,
direction: Int,
- previouslyFocusedRect: Rect?
+ previouslyFocusedRect: Rect?,
) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
if (borderEnabled) {
@@ -682,7 +691,7 @@
open fun bind(
task: Task,
orientedState: RecentsOrientedState,
- taskOverlayFactory: TaskOverlayFactory
+ taskOverlayFactory: TaskOverlayFactory,
) {
cancelPendingLoadTasks()
@@ -707,7 +716,7 @@
@IdRes iconViewId: Int,
@IdRes showWindowViewId: Int,
@StagePosition stagePosition: Int,
- taskOverlayFactory: TaskOverlayFactory
+ taskOverlayFactory: TaskOverlayFactory,
): TaskContainer {
val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = findViewById(thumbnailViewId)!!
val snapshotView =
@@ -774,10 +783,10 @@
* Updates TaskView scaling and translation required to support variable width if enabled, while
* ensuring TaskView fits into screen in fullscreen.
*/
- fun updateTaskSize(
+ open fun updateTaskSize(
lastComputedTaskSize: Rect,
lastComputedGridTaskSize: Rect,
- lastComputedCarouselTaskSize: Rect
+ lastComputedCarouselTaskSize: Rect,
) {
val thumbnailPadding = container.deviceProfile.overviewTaskThumbnailTopMarginPx
val taskWidth = lastComputedTaskSize.width()
@@ -789,9 +798,10 @@
if (container.deviceProfile.isTablet) {
val boxWidth: Int
val boxHeight: Int
- if (isFocusedTask) {
- // Task will be focused and should use focused task size. Use focusTaskRatio
- // that is associated with the original orientation of the focused task.
+
+ // Focused task and Desktop tasks should use focusTaskRatio that is associated
+ // with the original orientation of the focused task.
+ if (isLargeTile) {
boxWidth = taskWidth
boxHeight = taskHeight
} else {
@@ -1334,7 +1344,7 @@
private fun computeAndSetIconTouchDelegate(
view: TaskViewIcon,
tempCenterCoordinates: FloatArray,
- transformingTouchDelegate: TransformingTouchDelegate
+ transformingTouchDelegate: TransformingTouchDelegate,
) {
val viewHalfWidth = view.width / 2f
val viewHalfHeight = view.height / 2f
@@ -1397,7 +1407,7 @@
private fun onFocusTransitionProgressUpdated(focusTransitionProgress: Float) {
taskContainers.forEach {
it.iconView.setContentAlpha(focusTransitionProgress)
- it.digitalWellBeingToast?.updateBannerOffset(1f - focusTransitionProgress)
+ it.digitalWellBeingToast?.bannerOffsetPercentage = 1f - focusTransitionProgress
}
}
@@ -1547,7 +1557,7 @@
private fun onModalnessUpdated(modalness: Float) {
taskContainers.forEach {
it.iconView.setModalAlpha(1 - modalness)
- it.digitalWellBeingToast?.updateBannerOffset(modalness)
+ it.digitalWellBeingToast?.bannerOffsetPercentage = modalness
}
}
@@ -1583,7 +1593,7 @@
}
dismissScale = 1f
translationZ = 0f
- alpha = stableAlpha
+ attachAlpha = 1f
setIconScaleAndDim(1f)
setColorTint(0f, 0)
}
@@ -1660,6 +1670,11 @@
const val FOCUS_TRANSITION_INDEX_SCALE_AND_DIM = 1
const val FOCUS_TRANSITION_INDEX_COUNT = 2
+ private const val ALPHA_INDEX_STABLE = 0
+ private const val ALPHA_INDEX_ATTACH = 1
+
+ private const val NUM_ALPHA_CHANNELS = 2
+
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
const val MAX_PAGE_SCRIM_ALPHA = 0.4f
const val SCALE_ICON_DURATION: Long = 120
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarDesktopModeControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarDesktopModeControllerTest.kt
new file mode 100644
index 0000000..72bbfc9
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarDesktopModeControllerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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
+
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.taskbar.TaskbarBackgroundRenderer.Companion.MAX_ROUNDNESS
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@LauncherMultivalentJUnit.EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+class TaskbarDesktopModeControllerTest {
+
+ private val context =
+ TaskbarWindowSandboxContext.create(
+ InstrumentationRegistry.getInstrumentation().targetContext
+ )
+
+ @get:Rule(order = 0) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @TaskbarUnitTestRule.InjectController
+ lateinit var taskbarDesktopModeController: TaskbarDesktopModeController
+
+ @Test
+ fun whenTaskbarRequiresCornerRoundness_shouldReturnDefaultCornerRoundness() {
+ assertThat(taskbarDesktopModeController.getTaskbarCornerRoundness(true))
+ .isEqualTo(MAX_ROUNDNESS)
+ }
+
+ @Test
+ fun whenTaskbarRequiresCornerRoundness_shouldReturnZeroAsCornerRoundness() {
+ assertThat(taskbarDesktopModeController.getTaskbarCornerRoundness(false)).isEqualTo(0f)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewControllerTest.kt
new file mode 100644
index 0000000..4aac1dc
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewControllerTest.kt
@@ -0,0 +1,233 @@
+/*
+ * 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
+
+import android.view.View
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.taskbar.TaskbarViewController.DIVIDER_VIEW_POSITION_OFFSET
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+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.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+/**
+ * Legend for the comments below:
+ * A: All Apps Button
+ * H: Hotseat item
+ * |: Divider
+ * R: Recent item
+ *
+ * The comments are formatted in two lines:
+ * // Items in taskbar, e.g. A | HHHHHH
+ * // Index of items relative to Hotseat: -1 -.5 012345
+ */
+class TaskbarViewControllerTest {
+
+ private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
+ @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var taskbarViewController: TaskbarViewController
+
+ @Test
+ fun testGetPositionInHotseat_allAppsButton_nonRtl() {
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ 6,
+ /* child = */ View(context),
+ /* isRtl = */ false,
+ /* isAllAppsButton = */ true,
+ /* isTaskbarDividerView = */ false,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ -1
+ )
+ // [>A<] | [HHHHHH]
+ // -1 -.5 012345
+ assertThat(position).isEqualTo(-1)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_allAppsButton_rtl() {
+ val numShownHotseatIcons = 6
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ true,
+ /* isAllAppsButton = */ true,
+ /* isTaskbarDividerView = */ false,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ -1
+ )
+ // [HHHHHH] | [>A<]
+ // 012345 5.5 6
+ assertThat(position).isEqualTo(numShownHotseatIcons)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_dividerView_notForRecents_nonRtl() {
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ 6,
+ /* child = */ View(context),
+ /* isRtl = */ false,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ true,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ -1
+ )
+ // [A] >|< [HHHHHH]
+ // -1 -.5 012345
+ assertThat(position).isEqualTo(-DIVIDER_VIEW_POSITION_OFFSET)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_dividerView_forRecents_nonRtl() {
+ val numShownHotseatIcons = 6
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ false,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ true,
+ /* isDividerForRecents = */ true,
+ /* recentTaskIndex = */ -1
+ )
+ // [A] [HHHHHH] >|< [RR]
+ // -1 012345 5.5 67
+ assertThat(position).isEqualTo(numShownHotseatIcons - DIVIDER_VIEW_POSITION_OFFSET)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_dividerView_notForRecents_rtl() {
+ val numShownHotseatIcons = 6
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ true,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ true,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ -1
+ )
+ // [HHHHHH] >|< [A]
+ // 012345 5.5 6
+ assertThat(position).isEqualTo(numShownHotseatIcons - DIVIDER_VIEW_POSITION_OFFSET)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_dividerView_forRecents_rtl() {
+ val numShownHotseatIcons = 6
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ true,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ true,
+ /* isDividerForRecents = */ true,
+ /* recentTaskIndex = */ -1
+ )
+ // [HHHHHH][A] >|< [RR]
+ // 012345 6 6.5 78
+ assertThat(position).isEqualTo(numShownHotseatIcons + DIVIDER_VIEW_POSITION_OFFSET)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_recentTasks_firstRecentIndex_nonRtl() {
+ val numShownHotseatIcons = 6
+ val recentTaskIndex = 0
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ false,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ false,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ recentTaskIndex
+ )
+ // [A][HHHHHH] | [>R<R]
+ // -1 012345 5.5 6 7
+ assertThat(position).isEqualTo(numShownHotseatIcons + recentTaskIndex)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_recentTasks_secondRecentIndex_nonRtl() {
+ val numShownHotseatIcons = 6
+ val recentTaskIndex = 1
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ false,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ false,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ recentTaskIndex
+ )
+ // [A][HHHHHH] | [R>R<]
+ // -1 012345 5.5 6 7
+ assertThat(position).isEqualTo(numShownHotseatIcons + recentTaskIndex)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_recentTasks_firstRecentIndex_rtl() {
+ val numShownHotseatIcons = 6
+ val recentTaskIndex = 0
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ true,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ false,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ recentTaskIndex
+ )
+ // [HHHHHH][A] | [>R<R]
+ // 012345 6 6.5 7 8
+ assertThat(position).isEqualTo(numShownHotseatIcons + 1 + recentTaskIndex)
+ }
+
+ @Test
+ fun testGetPositionInHotseat_recentTasks_secondRecentIndex_rtl() {
+ val numShownHotseatIcons = 6
+ val recentTaskIndex = 1
+ val position =
+ taskbarViewController.getPositionInHotseat(
+ /* numShownHotseatIcons = */ numShownHotseatIcons,
+ /* child = */ View(context),
+ /* isRtl = */ true,
+ /* isAllAppsButton = */ false,
+ /* isTaskbarDividerView = */ false,
+ /* isDividerForRecents = */ false,
+ /* recentTaskIndex = */ recentTaskIndex
+ )
+ // [HHHHHH][A] | [R>R<]
+ // 012345 6 6.5 7 8
+ assertThat(position).isEqualTo(numShownHotseatIcons + 1 + recentTaskIndex)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt
new file mode 100644
index 0000000..785ec66
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleBarInputConsumerTest.kt
@@ -0,0 +1,156 @@
+/*
+ * 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
+
+import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
+import com.android.quickstep.inputconsumers.BubbleBarInputConsumer
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for bubble bar input consumer, namely the static method that indicates whether the input
+ * consumer should handle the event.
+ */
+@RunWith(AndroidJUnit4::class)
+class BubbleBarInputConsumerTest {
+
+ private lateinit var bubbleControllers: BubbleControllers
+
+ @Mock private lateinit var taskbarActivityContext: TaskbarActivityContext
+ @Mock private lateinit var bubbleBarController: BubbleBarController
+ @Mock private lateinit var bubbleBarViewController: BubbleBarViewController
+ @Mock private lateinit var bubbleStashController: BubbleStashController
+ @Mock private lateinit var bubbleStashedHandleViewController: BubbleStashedHandleViewController
+ @Mock private lateinit var bubbleDragController: BubbleDragController
+ @Mock private lateinit var bubbleDismissController: BubbleDismissController
+ @Mock private lateinit var bubbleBarPinController: BubbleBarPinController
+ @Mock private lateinit var bubblePinController: BubblePinController
+ @Mock private lateinit var bubbleCreator: BubbleCreator
+
+ @Mock private lateinit var motionEvent: MotionEvent
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ bubbleControllers =
+ BubbleControllers(
+ bubbleBarController,
+ bubbleBarViewController,
+ bubbleStashController,
+ Optional.of(bubbleStashedHandleViewController),
+ bubbleDragController,
+ bubbleDismissController,
+ bubbleBarPinController,
+ bubblePinController,
+ bubbleCreator
+ )
+ }
+
+ @Test
+ fun testIsEventOnBubbles_noTaskbarActivityContext() {
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(null, motionEvent)).isFalse()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_bubblesNotEnabled() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(false)
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isFalse()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_noBubbleControllers() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
+ whenever(taskbarActivityContext.bubbleControllers).thenReturn(null)
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isFalse()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_noBubbles() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
+ whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isFalse()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_eventOnStashedHandle() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
+ whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ whenever(bubbleStashController.isStashed).thenReturn(true)
+ whenever(bubbleStashedHandleViewController.isEventOverHandle(any())).thenReturn(true)
+
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isTrue()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_eventNotOnStashedHandle() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
+ whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ whenever(bubbleStashController.isStashed).thenReturn(true)
+ whenever(bubbleStashedHandleViewController.isEventOverHandle(any())).thenReturn(false)
+
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isFalse()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_eventOnVisibleBubbleView() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
+ whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ whenever(bubbleStashController.isStashed).thenReturn(false)
+ whenever(bubbleBarViewController.isBubbleBarVisible).thenReturn(true)
+ whenever(bubbleBarViewController.isEventOverBubbleBar(any())).thenReturn(true)
+
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isTrue()
+ }
+
+ @Test
+ fun testIsEventOnBubbles_eventNotOnVisibleBubbleView() {
+ whenever(taskbarActivityContext.isBubbleBarEnabled).thenReturn(true)
+ whenever(taskbarActivityContext.bubbleControllers).thenReturn(bubbleControllers)
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ whenever(bubbleStashController.isStashed).thenReturn(false)
+ whenever(bubbleBarViewController.isBubbleBarVisible).thenReturn(true)
+ whenever(bubbleBarViewController.isEventOverBubbleBar(any())).thenReturn(false)
+
+ assertThat(BubbleBarInputConsumer.isEventOnBubbles(taskbarActivityContext, motionEvent))
+ .isFalse()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 4da06e1..7928ce9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -64,6 +64,7 @@
private lateinit var bubble: BubbleBarBubble
private lateinit var bubbleBarView: BubbleBarView
private lateinit var bubbleStashController: BubbleStashController
+ private val onExpandedNoOp = Runnable {}
@Before
fun setUp() {
@@ -81,7 +82,12 @@
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -125,7 +131,12 @@
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -168,7 +179,12 @@
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -208,7 +224,12 @@
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -249,7 +270,12 @@
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -278,8 +304,15 @@
val handleAnimator = PhysicsAnimator.getInstance(handle)
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = true)
@@ -303,6 +336,7 @@
assertThat(animatorScheduler.delayedBlock).isNull()
verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -314,8 +348,15 @@
val handleAnimator = PhysicsAnimator.getInstance(handle)
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -345,6 +386,7 @@
.isEqualTo(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS + BAR_TRANSLATION_Y_FOR_TASKBAR)
verifyBubbleBarIsExpandedWithTranslation(BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(animator.isAnimating).isFalse()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -356,8 +398,15 @@
val handleAnimator = PhysicsAnimator.getInstance(handle)
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleInForStashed(bubble, isExpanding = false)
@@ -384,6 +433,7 @@
.isEqualTo(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS + BAR_TRANSLATION_Y_FOR_TASKBAR)
verifyBubbleBarIsExpandedWithTranslation(BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(animator.isAnimating).isFalse()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -400,7 +450,12 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateToInitialState(bubble, isInApp = true, isExpanding = false)
@@ -442,8 +497,15 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateToInitialState(bubble, isInApp = true, isExpanding = true)
@@ -459,6 +521,7 @@
assertThat(animatorScheduler.delayedBlock).isNull()
verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -471,7 +534,12 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateToInitialState(bubble, isInApp = false, isExpanding = false)
@@ -502,8 +570,15 @@
whenever(bubbleStashController.bubbleBarTranslationY)
.thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateToInitialState(bubble, isInApp = false, isExpanding = false)
@@ -533,6 +608,7 @@
verifyBubbleBarIsExpandedWithTranslation(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(animator.isAnimating).isFalse()
verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -542,8 +618,15 @@
whenever(bubbleStashController.bubbleBarTranslationY)
.thenReturn(BAR_TRANSLATION_Y_FOR_HOTSEAT)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateToInitialState(bubble, isInApp = false, isExpanding = false)
@@ -566,6 +649,7 @@
verifyBubbleBarIsExpandedWithTranslation(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(animator.isAnimating).isFalse()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -578,7 +662,12 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpandedNoOp,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleBarForCollapsed(bubble, isExpanding = false)
@@ -617,8 +706,15 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleBarForCollapsed(bubble, isExpanding = true)
@@ -645,6 +741,7 @@
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(bubbleBarView.isExpanded).isTrue()
verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -656,8 +753,15 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleBarForCollapsed(bubble, isExpanding = false)
@@ -695,6 +799,7 @@
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(bubbleBarView.isExpanded).isTrue()
verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(notifiedExpanded).isTrue()
}
@Test
@@ -706,8 +811,15 @@
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
+ var notifiedExpanded = false
+ val onExpanded = Runnable { notifiedExpanded = true }
val animator =
- BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
+ BubbleBarViewAnimator(
+ bubbleBarView,
+ bubbleStashController,
+ onExpanded,
+ animatorScheduler
+ )
InstrumentationRegistry.getInstrumentation().runOnMainSync {
animator.animateBubbleBarForCollapsed(bubble, isExpanding = false)
@@ -742,6 +854,7 @@
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_HOTSEAT)
assertThat(bubbleBarView.isExpanded).isTrue()
verify(bubbleStashController).showBubbleBarImmediate()
+ assertThat(notifiedExpanded).isTrue()
}
private fun setUpBubbleBar() {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt
new file mode 100644
index 0000000..7d09efd
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeHighResLoadingStateNotifier.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.recents.data
+
+import com.android.quickstep.TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback
+
+class FakeHighResLoadingStateNotifier : HighResLoadingStateNotifier {
+ val listeners = mutableListOf<HighResLoadingStateChangedCallback>()
+
+ override fun addCallback(callback: HighResLoadingStateChangedCallback) {
+ listeners.add(callback)
+ }
+
+ override fun removeCallback(callback: HighResLoadingStateChangedCallback) {
+ listeners.remove(callback)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
index cdfbd16..fc2f029 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
@@ -20,8 +20,6 @@
private var recentsDeviceProfile =
RecentsDeviceProfile(
isLargeScreen = false,
- widthPx = 1080,
- heightPx = 1920,
)
override fun getRecentsDeviceProfile() = recentsDeviceProfile
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
index fee4979..5de876a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
@@ -27,7 +27,8 @@
class FakeTaskIconDataSource : TaskIconDataSource {
- val taskIdToDrawable: Map<Int, Drawable> = (0..10).associateWith { mockCopyableDrawable() }
+ val taskIdToDrawable: MutableMap<Int, Drawable> =
+ (0..10).associateWith { mockCopyableDrawable() }.toMutableMap()
val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
var shouldLoadSynchronously: Boolean = true
@@ -52,15 +53,17 @@
return null
}
- private fun mockCopyableDrawable(): Drawable {
- val mutableDrawable = mock<Drawable>()
- val immutableDrawable =
- mock<Drawable>().apply { whenever(mutate()).thenReturn(mutableDrawable) }
- val constantState =
- mock<Drawable.ConstantState>().apply {
- whenever(newDrawable()).thenReturn(immutableDrawable)
- }
- return mutableDrawable.apply { whenever(this.constantState).thenReturn(constantState) }
+ companion object {
+ fun mockCopyableDrawable(): Drawable {
+ val mutableDrawable = mock<Drawable>()
+ val immutableDrawable =
+ mock<Drawable>().apply { whenever(mutate()).thenReturn(mutableDrawable) }
+ val constantState =
+ mock<Drawable.ConstantState>().apply {
+ whenever(newDrawable()).thenReturn(immutableDrawable)
+ }
+ return mutableDrawable.apply { whenever(this.constantState).thenReturn(constantState) }
+ }
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
index 30fc491..d12c0b0 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
@@ -27,7 +27,8 @@
class FakeTaskThumbnailDataSource : TaskThumbnailDataSource {
- val taskIdToBitmap: Map<Int, Bitmap> = (0..10).associateWith { mock() }
+ val taskIdToBitmap: MutableMap<Int, Bitmap> =
+ (0..10).associateWith { mock<Bitmap>() }.toMutableMap()
val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
var shouldLoadSynchronously: Boolean = true
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskVisualsChangeNotifier.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskVisualsChangeNotifier.kt
new file mode 100644
index 0000000..765f0d1
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskVisualsChangeNotifier.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.recents.data
+
+import com.android.quickstep.util.TaskVisualsChangeListener
+
+class FakeTaskVisualsChangeNotifier : TaskVisualsChangeNotifier {
+ val listeners = mutableListOf<TaskVisualsChangeListener>()
+
+ override fun addThumbnailChangeListener(listener: TaskVisualsChangeListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeThumbnailChangeListener(listener: TaskVisualsChangeListener) {
+ listeners.remove(listener)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
index d94a351..7a17872 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
@@ -28,7 +28,6 @@
private var taskIconDataMap: Map<Int, TaskIconQueryResponse> = emptyMap()
private var tasks: MutableStateFlow<List<Task>> = MutableStateFlow(emptyList())
private var visibleTasks: MutableStateFlow<List<Int>> = MutableStateFlow(emptyList())
- private var thumbnailOverrideMap: Map<Int, ThumbnailData> = emptyMap()
override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> = tasks
@@ -39,7 +38,7 @@
.map { taskList ->
val task = taskList.firstOrNull { it.key.id == taskId } ?: return@map null
Task(task).apply {
- thumbnail = thumbnailOverrideMap[taskId] ?: task.thumbnail
+ thumbnail = task.thumbnail
icon = task.icon
titleDescription = task.titleDescription
title = task.title
@@ -62,16 +61,6 @@
}
}
}
- setThumbnailOverrideInternal(thumbnailOverrideMap)
- }
-
- override fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
- setThumbnailOverrideInternal(thumbnailOverride)
- }
-
- private fun setThumbnailOverrideInternal(thumbnailOverride: Map<Int, ThumbnailData>) {
- thumbnailOverrideMap =
- thumbnailOverride.filterKeys(this.visibleTasks.value::contains).toMap()
}
fun seedTasks(tasks: List<Task>) {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
index e74fe4b..abe4142 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
@@ -39,6 +39,6 @@
whenever(recentsViewContainer.deviceProfile).thenReturn(tabletDeviceProfile)
assertThat(systemUnderTest.getRecentsDeviceProfile())
- .isEqualTo(RecentsDeviceProfile(isLargeScreen = true, widthPx = 1600, heightPx = 2560))
+ .isEqualTo(RecentsDeviceProfile(isLargeScreen = true))
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
new file mode 100644
index 0000000..41f6bfd
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TaskVisualsChangedDelegateTest.kt
@@ -0,0 +1,191 @@
+/*
+ * 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.recents.data
+
+import android.content.ComponentName
+import android.content.Intent
+import android.os.UserHandle
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskIconChangedCallback
+import com.android.quickstep.recents.data.TaskVisualsChangedDelegate.TaskThumbnailChangedCallback
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+
+class TaskVisualsChangedDelegateTest {
+ private val taskVisualsChangeNotifier = FakeTaskVisualsChangeNotifier()
+ private val highResLoadingStateNotifier = FakeHighResLoadingStateNotifier()
+
+ val systemUnderTest =
+ TaskVisualsChangedDelegateImpl(taskVisualsChangeNotifier, highResLoadingStateNotifier)
+
+ @Test
+ fun addingFirstListener_addsListenerToNotifiers() {
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), mock())
+
+ assertThat(taskVisualsChangeNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ assertThat(highResLoadingStateNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ }
+
+ @Test
+ fun addingAndRemovingListener_removesListenerFromNotifiers() {
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), mock())
+ systemUnderTest.unregisterTaskThumbnailChangedCallback(createTaskKey(id = 1))
+
+ assertThat(taskVisualsChangeNotifier.listeners).isEmpty()
+ assertThat(highResLoadingStateNotifier.listeners).isEmpty()
+ }
+
+ @Test
+ fun addingTwoAndRemovingOneListener_doesNotRemoveListenerFromNotifiers() {
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), mock())
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 2), mock())
+ systemUnderTest.unregisterTaskThumbnailChangedCallback(createTaskKey(id = 1))
+
+ assertThat(taskVisualsChangeNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ assertThat(highResLoadingStateNotifier.listeners.single()).isEqualTo(systemUnderTest)
+ }
+
+ @Test
+ fun onTaskIconChangedWithTaskId_notifiesCorrectListenerOnly() {
+ val expectedListener = mock<TaskIconChangedCallback>()
+ val additionalListener = mock<TaskIconChangedCallback>()
+ systemUnderTest.registerTaskIconChangedCallback(createTaskKey(id = 1), expectedListener)
+ systemUnderTest.registerTaskIconChangedCallback(createTaskKey(id = 2), additionalListener)
+
+ systemUnderTest.onTaskIconChanged(1)
+
+ verify(expectedListener).onTaskIconChanged()
+ verifyNoMoreInteractions(additionalListener)
+ }
+
+ @Test
+ fun onTaskIconChangedWithoutTaskId_notifiesCorrectListenerOnly() {
+ val expectedListener = mock<TaskIconChangedCallback>()
+ val listener = mock<TaskIconChangedCallback>()
+ // Correct match
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
+ expectedListener
+ )
+ // 1 out of 2 match
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 2, pkg = PACKAGE_NAME, userId = 1),
+ listener
+ )
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 3, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 2),
+ listener
+ )
+ // 0 out of 2 match
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 4, pkg = PACKAGE_NAME, userId = 2),
+ listener
+ )
+
+ systemUnderTest.onTaskIconChanged(ALTERNATIVE_PACKAGE_NAME, UserHandle(1))
+
+ verify(expectedListener).onTaskIconChanged()
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun replacedTaskIconChangedCallbacks_notCalled() {
+ val replacedListener = mock<TaskIconChangedCallback>()
+ val newListener = mock<TaskIconChangedCallback>()
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
+ replacedListener
+ )
+ systemUnderTest.registerTaskIconChangedCallback(
+ createTaskKey(id = 1, pkg = ALTERNATIVE_PACKAGE_NAME, userId = 1),
+ newListener
+ )
+
+ systemUnderTest.onTaskIconChanged(ALTERNATIVE_PACKAGE_NAME, UserHandle(1))
+
+ verifyNoMoreInteractions(replacedListener)
+ verify(newListener).onTaskIconChanged()
+ }
+
+ @Test
+ fun onTaskThumbnailChanged_notifiesCorrectListenerOnly() {
+ val expectedListener = mock<TaskThumbnailChangedCallback>()
+ val additionalListener = mock<TaskThumbnailChangedCallback>()
+ val expectedThumbnailData = ThumbnailData(snapshotId = 12345)
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 1),
+ expectedListener
+ )
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 2),
+ additionalListener
+ )
+
+ systemUnderTest.onTaskThumbnailChanged(1, expectedThumbnailData)
+
+ verify(expectedListener).onTaskThumbnailChanged(expectedThumbnailData)
+ verifyNoMoreInteractions(additionalListener)
+ }
+
+ @Test
+ fun onHighResLoadingStateChanged_notifiesAllListeners() {
+ val expectedListener = mock<TaskThumbnailChangedCallback>()
+ val additionalListener = mock<TaskThumbnailChangedCallback>()
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 1),
+ expectedListener
+ )
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 2),
+ additionalListener
+ )
+
+ systemUnderTest.onHighResLoadingStateChanged(true)
+
+ verify(expectedListener).onHighResLoadingStateChanged()
+ verify(additionalListener).onHighResLoadingStateChanged()
+ }
+
+ @Test
+ fun replacedTaskThumbnailChangedCallbacks_notCalled() {
+ val replacedListener1 = mock<TaskThumbnailChangedCallback>()
+ val newListener1 = mock<TaskThumbnailChangedCallback>()
+ val expectedThumbnailData = ThumbnailData(snapshotId = 12345)
+ systemUnderTest.registerTaskThumbnailChangedCallback(
+ createTaskKey(id = 1),
+ replacedListener1
+ )
+ systemUnderTest.registerTaskThumbnailChangedCallback(createTaskKey(id = 1), newListener1)
+
+ systemUnderTest.onTaskThumbnailChanged(1, expectedThumbnailData)
+
+ verifyNoMoreInteractions(replacedListener1)
+ verify(newListener1).onTaskThumbnailChanged(expectedThumbnailData)
+ }
+
+ private fun createTaskKey(id: Int = 1, pkg: String = PACKAGE_NAME, userId: Int = 1) =
+ TaskKey(id, 0, Intent().setPackage(pkg), ComponentName("", ""), userId, 0)
+
+ private companion object {
+ const val PACKAGE_NAME = "com.test.test"
+ const val ALTERNATIVE_PACKAGE_NAME = "com.test.test2"
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index e6534eb..f31467f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -19,7 +19,7 @@
import android.content.ComponentName
import android.content.Intent
import android.graphics.Bitmap
-import android.view.Surface
+import android.graphics.drawable.Drawable
import com.android.launcher3.util.TestDispatcherProvider
import com.android.quickstep.task.thumbnail.TaskThumbnailViewModelTest
import com.android.quickstep.util.DesktopTask
@@ -29,6 +29,9 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -48,6 +51,10 @@
private val recentsModel = FakeRecentTasksDataSource()
private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
private val taskIconDataSource = FakeTaskIconDataSource()
+ private val taskVisualsChangeNotifier = FakeTaskVisualsChangeNotifier()
+ private val highResLoadingStateNotifier = FakeHighResLoadingStateNotifier()
+ private val taskVisualsChangedDelegate =
+ TaskVisualsChangedDelegateImpl(taskVisualsChangeNotifier, highResLoadingStateNotifier)
private val dispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(dispatcher)
@@ -56,6 +63,7 @@
recentsModel,
taskThumbnailDataSource,
taskIconDataSource,
+ taskVisualsChangedDelegate,
testScope.backgroundScope,
TestDispatcherProvider(dispatcher)
)
@@ -203,87 +211,81 @@
}
@Test
- fun addThumbnailOverrideOverrideThumbnails() =
+ fun onTaskThumbnailChanged_setsNewThumbnailDataOnTask() =
testScope.runTest {
recentsModel.seedTasks(defaultTaskList)
- val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
- val thumbnailOverride2 = createThumbnailData()
- systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
-
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride2.thumbnail)
- }
-
- @Test
- fun addThumbnailOverrideMultipleOverrides() =
- testScope.runTest {
- recentsModel.seedTasks(defaultTaskList)
- val thumbnailOverride1 = createThumbnailData()
- val thumbnailOverride2 = createThumbnailData()
- val thumbnailOverride3 = createThumbnailData()
- systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(1 to thumbnailOverride1))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride3))
-
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride1.thumbnail)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride3.thumbnail)
- }
-
- @Test
- fun addThumbnailOverrideClearedWhenTaskBecomeInvisible() =
- testScope.runTest {
- recentsModel.seedTasks(defaultTaskList)
- val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
- val thumbnailOverride1 = createThumbnailData()
- val thumbnailOverride2 = createThumbnailData()
- systemUnderTest.getAllTaskData(forceRefresh = true)
-
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(1 to thumbnailOverride1))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride2))
- // Making task 2 invisible and visible again should clear the override
- systemUnderTest.setVisibleTasks(listOf(1))
- systemUnderTest.setVisibleTasks(listOf(1, 2))
-
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
- .isEqualTo(thumbnailOverride1.thumbnail)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
- }
-
- @Test
- fun addThumbnailOverrideDoesNotOverrideInvisibleTasks() =
- testScope.runTest {
- recentsModel.seedTasks(defaultTaskList)
- val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
- val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
- val thumbnailOverride = createThumbnailData()
systemUnderTest.getAllTaskData(forceRefresh = true)
systemUnderTest.setVisibleTasks(listOf(1))
- systemUnderTest.addOrUpdateThumbnailOverride(mapOf(2 to thumbnailOverride))
- systemUnderTest.setVisibleTasks(listOf(1, 2))
- assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail).isEqualTo(bitmap1)
- assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
+ val expectedThumbnailData = createThumbnailData()
+ val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+ val task1ThumbnailValues = mutableListOf<ThumbnailData?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.thumbnail }.toList(task1ThumbnailValues)
+ }
+ taskVisualsChangedDelegate.onTaskThumbnailChanged(1, expectedThumbnailData)
+
+ assertThat(task1ThumbnailValues[1]!!.thumbnail).isEqualTo(expectedPreviousBitmap)
+ assertThat(task1ThumbnailValues.last()).isEqualTo(expectedThumbnailData)
+ }
+
+ @Test
+ fun onHighResLoadingStateChanged_setsNewThumbnailDataOnTask() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1))
+
+ val expectedBitmap = mock<Bitmap>()
+ val expectedPreviousBitmap = taskThumbnailDataSource.taskIdToBitmap[1]
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+ val task1ThumbnailValues = mutableListOf<Bitmap?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.thumbnail?.thumbnail }.toList(task1ThumbnailValues)
+ }
+ taskThumbnailDataSource.taskIdToBitmap[1] = expectedBitmap
+ taskVisualsChangedDelegate.onHighResLoadingStateChanged(true)
+
+ assertThat(task1ThumbnailValues[1]).isEqualTo(expectedPreviousBitmap)
+ assertThat(task1ThumbnailValues.last()).isEqualTo(expectedBitmap)
+ }
+
+ @Test
+ fun onTaskIconChanged_setsNewIconOnTask() =
+ testScope.runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1))
+
+ val expectedIcon = FakeTaskIconDataSource.mockCopyableDrawable()
+ val expectedPreviousIcon = taskIconDataSource.taskIdToDrawable[1]
+ val taskDataFlow = systemUnderTest.getTaskDataById(1)
+
+ val task1IconValues = mutableListOf<Drawable?>()
+ testScope.backgroundScope.launch {
+ taskDataFlow.map { it?.icon }.toList(task1IconValues)
+ }
+ taskIconDataSource.taskIdToDrawable[1] = expectedIcon
+ taskVisualsChangedDelegate.onTaskIconChanged(1)
+
+ assertThat(task1IconValues[1]).isEqualTo(expectedPreviousIcon)
+ assertThat(task1IconValues.last()).isEqualTo(expectedIcon)
}
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000))
- private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): ThumbnailData {
+ private fun createThumbnailData(): ThumbnailData {
val bitmap = mock<Bitmap>()
whenever(bitmap.width).thenReturn(TaskThumbnailViewModelTest.THUMBNAIL_WIDTH)
whenever(bitmap.height).thenReturn(TaskThumbnailViewModelTest.THUMBNAIL_HEIGHT)
- return ThumbnailData(thumbnail = bitmap, rotation = rotation)
+ return ThumbnailData(thumbnail = bitmap)
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
index dc16475..fe67313 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
@@ -70,44 +70,6 @@
assertThat(thumbnailDataFlow2.first()).isNull()
}
- @Test
- fun thumbnailOverrideWaitAndReset() = runTest {
- val thumbnailData1 = createThumbnailData().apply { snapshotId = 1 }
- val thumbnailData2 = createThumbnailData().apply { snapshotId = 2 }
- tasksRepository.seedTasks(tasks)
- tasksRepository.seedThumbnailData(mapOf(1 to thumbnailData1, 2 to thumbnailData2))
-
- val thumbnailDataFlow1 = tasksRepository.getThumbnailById(1)
- val thumbnailDataFlow2 = tasksRepository.getThumbnailById(2)
-
- systemUnderTest.refreshAllTaskData()
- systemUnderTest.updateVisibleTasks(listOf(1, 2))
-
- assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
- assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
-
- systemUnderTest.setRunningTaskShowScreenshot(true)
- val thumbnailOverride = mapOf(2 to createThumbnailData().apply { snapshotId = 3 })
- systemUnderTest.addOrUpdateThumbnailOverride(thumbnailOverride)
-
- systemUnderTest.waitForRunningTaskShowScreenshotToUpdate()
- val expectedUpdate = mapOf(2 to createThumbnailData().apply { snapshotId = 3 })
- systemUnderTest.waitForThumbnailsToUpdate(expectedUpdate)
-
- assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
- assertThat(thumbnailDataFlow2.first()?.snapshotId).isEqualTo(3)
-
- systemUnderTest.onReset()
-
- assertThat(thumbnailDataFlow1.first()).isNull()
- assertThat(thumbnailDataFlow2.first()).isNull()
-
- systemUnderTest.updateVisibleTasks(listOf(1, 2))
-
- assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
- assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
- }
-
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
colorBackground = Color.argb(taskId, taskId, taskId, taskId)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCaseTest.kt
deleted file mode 100644
index 13e8b09..0000000
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCaseTest.kt
+++ /dev/null
@@ -1,84 +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.quickstep.task.thumbnail
-
-import android.graphics.Point
-import android.graphics.drawable.Drawable
-import com.android.quickstep.recents.data.FakeRecentsDeviceProfileRepository
-import com.android.quickstep.task.viewmodel.TaskViewData
-import com.android.quickstep.views.TaskViewType
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
-
-class GetSplashSizeUseCaseTest {
- private val taskThumbnailViewData = TaskThumbnailViewData()
- private val taskViewData = TaskViewData(TaskViewType.SINGLE)
- private val recentsDeviceProfileRepository = FakeRecentsDeviceProfileRepository()
- private val systemUnderTest =
- GetSplashSizeUseCase(taskThumbnailViewData, taskViewData, recentsDeviceProfileRepository)
-
- @Test
- fun execute_whenNoScaleRequired_returnsIntrinsicSize() {
- taskThumbnailViewData.width.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().widthPx
- taskThumbnailViewData.height.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().heightPx
-
- assertThat(systemUnderTest.execute(createIcon(100, 100))).isEqualTo(Point(100, 100))
- }
-
- @Test
- fun execute_whenThumbnailViewIsSmallerThanScreen_returnsScaledSize() {
- taskThumbnailViewData.width.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().widthPx / 2
- taskThumbnailViewData.height.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().heightPx / 2
-
- assertThat(systemUnderTest.execute(createIcon(100, 100))).isEqualTo(Point(50, 50))
- }
-
- @Test
- fun execute_whenThumbnailViewIsSmallerThanScreen_withNonGridScale_returnsScaledSize() {
- taskThumbnailViewData.width.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().widthPx / 2
- taskThumbnailViewData.height.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().heightPx / 2
- taskViewData.nonGridScale.value = 2f
-
- assertThat(systemUnderTest.execute(createIcon(100, 100))).isEqualTo(Point(25, 25))
- }
-
- @Test
- fun execute_whenThumbnailViewIsSmallerThanScreen_withThumbnailViewScale_returnsScaledSize() {
- taskThumbnailViewData.width.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().widthPx / 2
- taskThumbnailViewData.height.value =
- recentsDeviceProfileRepository.getRecentsDeviceProfile().heightPx / 2
- taskThumbnailViewData.scaleX.value = 2f
- taskThumbnailViewData.scaleY.value = 2f
-
- assertThat(systemUnderTest.execute(createIcon(100, 100))).isEqualTo(Point(25, 25))
- }
-
- private fun createIcon(width: Int, height: Int): Drawable =
- mock<Drawable>().apply {
- whenever(intrinsicWidth).thenReturn(width)
- whenever(intrinsicHeight).thenReturn(height)
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
index fe7d37a..fcf4e56 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
@@ -21,7 +21,6 @@
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Matrix
-import android.graphics.Point
import android.graphics.drawable.Drawable
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,7 +34,6 @@
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Splash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
@@ -46,10 +44,8 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -63,7 +59,6 @@
private val tasksRepository = FakeTasksRepository()
private val mGetThumbnailPositionUseCase = mock<GetThumbnailPositionUseCase>()
private val splashAlphaUseCase: SplashAlphaUseCase = mock()
- private val getSplashSizeUseCase: GetSplashSizeUseCase = mock()
private val systemUnderTest by lazy {
TaskThumbnailViewModel(
recentsViewData,
@@ -72,17 +67,11 @@
tasksRepository,
mGetThumbnailPositionUseCase,
splashAlphaUseCase,
- getSplashSizeUseCase,
)
}
private val tasks = (0..5).map(::createTaskWithId)
- @Before
- fun setUp() {
- whenever(getSplashSizeUseCase.execute(any())).thenReturn(Point())
- }
-
@Test
fun initialStateIsUninitialized() = runTest {
assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
@@ -120,7 +109,7 @@
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_0,
),
- Splash(expectedIconData.icon, Point())
+ expectedIconData.icon
)
)
}
@@ -215,7 +204,7 @@
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_270,
),
- Splash(expectedIconData.icon, Point())
+ expectedIconData.icon
)
)
}
@@ -241,29 +230,12 @@
bitmap = expectedThumbnailData.thumbnail!!,
thumbnailRotation = Surface.ROTATION_0,
),
- Splash(expectedIconData.icon, Point())
+ expectedIconData.icon
)
)
}
@Test
- fun bindStoppedTask_thenStateContainsSplashSizeFromUseCase() = runTest {
- val taskId = 2
- val expectedSplashSize = Point(100, 150)
- whenever(getSplashSizeUseCase.execute(any())).thenReturn(expectedSplashSize)
- val expectedThumbnailData = createThumbnailData(rotation = Surface.ROTATION_270)
- tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
- val expectedIconData = createIconData("Task 2")
- tasksRepository.seedIconData(mapOf(taskId to expectedIconData))
- tasksRepository.seedTasks(tasks)
- tasksRepository.setVisibleTasks(listOf(taskId))
-
- systemUnderTest.bind(taskId)
- val uiState = systemUnderTest.uiState.first() as SnapshotSplash
- assertThat(uiState.splash.size).isEqualTo(expectedSplashSize)
- }
-
- @Test
fun getSnapshotMatrix_MissingThumbnail() = runTest {
val taskId = 2
val isRtl = true
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index 15b1e53..d064f4a 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -57,6 +57,7 @@
@Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
@Mock lateinit var taskbarPinningController: TaskbarPinningController
@Mock lateinit var optionalBubbleControllers: Optional<BubbleControllers>
+ @Mock lateinit var taskbarDesktopModeController: TaskbarDesktopModeController
lateinit var taskbarControllers: TaskbarControllers
@@ -98,6 +99,7 @@
keyboardQuickSwitchController,
taskbarPinningController,
optionalBubbleControllers,
+ taskbarDesktopModeController
)
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index d9d5585..885a7f6 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -39,8 +39,8 @@
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.common.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index cbc8441..244b897 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -39,7 +39,7 @@
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.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
import org.junit.Before;
import org.junit.Test;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
index 6e25b10..e981570 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplDigitalWellBeingToastTest.java
@@ -67,15 +67,15 @@
mLauncher.goHome();
final DigitalWellBeingToast toast = getToast();
- waitForLauncherCondition("Toast is not visible", launcher -> toast.hasLimit());
- assertEquals("Toast text: ", "5 minutes left today", toast.getText());
+ waitForLauncherCondition("Toast is not visible", launcher -> toast.getHasLimit());
+ assertEquals("Toast text: ", "5 minutes left today", toast.getBannerText());
// Unset time limit for app.
runWithShellPermission(
() -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
mLauncher.goHome();
- assertFalse("Toast is visible", getToast().hasLimit());
+ assertFalse("Toast is visible", getToast().getHasLimit());
} finally {
runWithShellPermission(
() -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
diff --git a/res/drawable/add_item_dialog_background.xml b/res/drawable/add_item_dialog_background.xml
index be4765a..39af989 100644
--- a/res/drawable/add_item_dialog_background.xml
+++ b/res/drawable/add_item_dialog_background.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
- <solid android:color="?attr/materialColorSurfaceContainerHighest" />
+ <solid android:color="?attr/widgetPickerPrimarySurfaceColor" />
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="?android:attr/dialogCornerRadius" />
diff --git a/res/drawable/bg_letter_list_text.xml b/res/drawable/bg_letter_list_text.xml
new file mode 100644
index 0000000..427702b
--- /dev/null
+++ b/res/drawable/bg_letter_list_text.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="?attr/materialColorSurfaceContainer" />
+ <corners android:radius="100dp"/>
+ <size
+ android:width="@dimen/bg_letter_list_text_size"
+ android:height="@dimen/bg_letter_list_text_size"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/widget_picker_tabs_background.xml b/res/drawable/widget_picker_tabs_background.xml
index a874dd8..f6607b7 100644
--- a/res/drawable/widget_picker_tabs_background.xml
+++ b/res/drawable/widget_picker_tabs_background.xml
@@ -13,36 +13,39 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/accent_ripple_color">
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetBottom="@dimen/widget_apps_tabs_vertical_padding"
+ android:insetTop="@dimen/widget_apps_tabs_vertical_padding">
+ <ripple
+ android:color="@color/accent_ripple_color">
- <item android:id="@android:id/mask">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
- <solid android:color="@color/accent_ripple_color" />
- </shape>
- </item>
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+ <solid android:color="@color/accent_ripple_color" />
+ </shape>
+ </item>
- <item>
- <selector android:enterFadeDuration="100">
- <item
- android:id="@+id/unselected"
- android:state_selected="false">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
- <solid android:color="?attr/widgetPickerTabBackgroundUnselected"/>
- </shape>
- </item>
+ <item>
+ <selector android:enterFadeDuration="100">
+ <item
+ android:id="@+id/unselected"
+ android:state_selected="false">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+ <solid android:color="?attr/widgetPickerTabBackgroundUnselected" />
+ </shape>
+ </item>
- <item
- android:id="@+id/selected"
- android:state_selected="true">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
- <solid android:color="?attr/widgetPickerTabBackgroundSelected"/>
- </shape>
- </item>
- </selector>
- </item>
-
-</ripple>
\ No newline at end of file
+ <item
+ android:id="@+id/selected"
+ android:state_selected="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+ <solid android:color="?attr/widgetPickerTabBackgroundSelected" />
+ </shape>
+ </item>
+ </selector>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
index 0f1d933..7e16ca5 100644
--- a/res/layout/all_apps_fast_scroller.xml
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -36,4 +36,17 @@
android:layout_marginEnd="@dimen/fastscroll_end_margin"
launcher:canThumbDetach="true" />
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/scroll_letter_layout"
+ android:layout_width="@dimen/fastscroll_width"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignTop="@+id/all_apps_header"
+ android:layout_marginTop="@dimen/all_apps_header_bottom_padding"
+ android:layout_marginEnd="@dimen/fastscroll_list_letter_end_margin"
+ android:clipToPadding="false"
+ android:outlineProvider="none"
+ />
</merge>
\ No newline at end of file
diff --git a/res/layout/fast_scroller_letter_list_text_view.xml b/res/layout/fast_scroller_letter_list_text_view.xml
new file mode 100644
index 0000000..493b6fc
--- /dev/null
+++ b/res/layout/fast_scroller_letter_list_text_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<com.android.launcher3.allapps.LetterListTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/fastscroll_list_letter_size"
+ android:layout_height="@dimen/fastscroll_list_letter_size"
+ android:textSize="@dimen/fastscroll_list_letter_text_size"
+ android:importantForAccessibility="no"
+ android:gravity="center"
+ android:clickable="false">
+</com.android.launcher3.allapps.LetterListTextView>
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index a709fbc..83c8d6c 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -51,7 +51,7 @@
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
- <com.android.launcher3.pageindicators.WorkspacePageIndicator
+ <com.android.launcher3.pageindicators.PageIndicatorDots
android:id="@+id/page_indicator"
android:layout_width="match_parent"
android:layout_height="@dimen/workspace_page_indicator_height"
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
deleted file mode 100644
index d5fe51e..0000000
--- a/res/layout/page_indicator_dots.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/page_indicator"
- android:layout_width="match_parent"
- android:layout_height="@dimen/workspace_page_indicator_height"
- android:layout_gravity="bottom | center_horizontal"
- android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 8dc785a..622f0d6 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -104,7 +104,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/widget_picker_tabs_background"
android:text="@string/widgets_full_sheet_personal_tab"
@@ -117,7 +116,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/widget_picker_tabs_background"
android:text="@string/widgets_full_sheet_work_tab"
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 1f41680..1cbd2ba 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -115,7 +115,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/widget_picker_tabs_background"
android:text="@string/widgets_full_sheet_personal_tab"
@@ -128,7 +127,6 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
- android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
android:layout_weight="1"
android:background="@drawable/widget_picker_tabs_background"
android:text="@string/widgets_full_sheet_work_tab"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 8b4b26a..490a7c2 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installeer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vasspeldvoorspelling"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Borrel"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lees tuis-instellings en -kortpaaie"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 03f62bf..7292eec 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ጫን"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"መተግበሪያውን አይጠቁሙ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"የፒን ግምት"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"አረፋ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 31ebd34..06fc0a8 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"تثبيت"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"عدم اقتراح التطبيق"</string>
<string name="pin_prediction" msgid="4196423321649756498">"تثبيت التطبيق المتوقّع"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"فقاعة"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"الاطلاع على الإعدادات والاختصارات على الشاشة الرئيسية"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index e2f4ecd..cd6e347 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ইনষ্টল কৰক"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"পৰামৰ্শ নিদিব"</string>
<string name="pin_prediction" msgid="4196423321649756498">"পূৰ্বানুমান কৰা এপ্টো পিন কৰক"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"বাবল"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শ্বৰ্টকাট ইনষ্টল কৰিব পাৰে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ব্য়ৱহাৰকাৰীৰ হস্তক্ষেপ অবিহনেই কোনো এপক শ্বৰ্টকাটবোৰ যোগ কৰাৰ অনুমতি দিয়ে।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"গৃহ স্ক্ৰীনত ছেটিং আৰু শ্বৰ্টকাটসমূহ পঢ়া"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 520295e..b8d660f 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Quraşdırın"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Tətbiq təklif olunmasın"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Proqnozlaşdırılan tətbiqi bərkidin"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Qabarcıq"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Əsas səhifə ayarlarını və qısayollarını oxumaq"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 09ddf76..4d4764e 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čitanje podešavanja i prečica na početnom ekranu"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 85792f7..641509e 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Усталяваць"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не прапаноўваць праграму"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Замацаваць прапанаваную праграму"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Бурбалка"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Стварэнне ярлыкоў"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"счытваць налады і ярлыкі на галоўным экране"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index baf405d..3ce3c5f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Инсталиране"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Без предлагане на приложение"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Фиксиране на предвиждането"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Балонче"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"четене на настройките и преките пътища на началния екран"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index a6bdde1..9b23590 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ইনস্টল করুন"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"অ্যাপ সাজেস্ট করবেন না"</string>
<string name="pin_prediction" msgid="4196423321649756498">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ পিন করুন"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"বাবল"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"হোম স্ক্রিনে সেটিংস ও শর্টকাট পড়ুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 0de15da..4a34da7 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čita postavke na početnom ekranu i prečice"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 18753ba..c341ec7 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instal·la"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No suggereixis l\'aplicació"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixa la predicció"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bombolla"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"llegir la configuració i les dreceres de la pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index e2e5ebf..d3512c9 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Nainstalovat"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovat aplikaci"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Připnout předpověď"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bublat"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čtení nastavení a zkratek plochy"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 31b6437..8aae860 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Foreslå ikke en app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fastgør forslaget"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Boble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"læs indstillinger og genveje for startskærm"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 2abbf4c..374f5a1 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installieren"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"App nicht vorschlagen"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vorgeschlagene App fixieren"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index d4322b1..cafe86e 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Εγκατάσταση"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Να μην προτείνεται"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Καρφίτσωμα πρόβλεψης"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Συννεφάκι"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 33a3c4d..1b0722d 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 33a3c4d..1b0722d 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 33a3c4d..1b0722d 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"read Home settings and shortcuts"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 793e5f2..ba1b0af 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"leer parámetros de configuración y accesos directos de la página principal"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 01b053c..ad12192 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir aplicación"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Burbuja"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"leer ajustes y accesos directos de la pantalla de inicio"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 231d0f7..96d0b2c 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Mull"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"avakuva seadete ja otseteede lugemine"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 1c734b1..bc9b8c1 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Burbuila"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzeko baimena ematen die aplikazioei."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"irakurri hasierako pantailako ezarpenak eta lasterbideak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a8a5491..c167194 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -24,7 +24,7 @@
<string name="activity_not_found" msgid="8071924732094499514">"برنامه نصب نشده است."</string>
<string name="activity_not_available" msgid="7456344436509528827">"برنامه در دسترس نیست"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"برنامه بارگیری شده در حالت ایمن غیرفعال شد"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"ابزارکها در حالت ایمن غیرفعال هستند"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"ابزارهها در حالت ایمن غیرفعال هستند"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"میانبر دردسترس نیست"</string>
<string name="home_screen" msgid="5629429142036709174">"صفحه اصلی"</string>
<string name="set_default_home_app" msgid="5808906607627586381">"تنظیم <xliff:g id="LAUNCHER_NAME">%1$s</xliff:g> بهعنوان برنامه صفحه اصلی پیشفرض در «تنظیمات»"</string>
@@ -36,40 +36,40 @@
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"از این جفت برنامه در این دستگاه پشتیبانی نمیشود"</string>
<string name="app_pair_needs_unfold" msgid="4588897528143807002">"برای استفاده از این جفت برنامه، دستگاه را باز کنید"</string>
<string name="app_pair_not_available" msgid="3556767440808032031">"جفت برنامه دردسترس نیست"</string>
- <string name="long_press_widget_to_add" msgid="3587712543577675817">"برای جابهجا کردن ابزارک، لمس کنید و نگه دارید."</string>
- <string name="long_accessible_way_to_add" msgid="2733588281439571974">"برای جابهجا کردن ابزارک یا استفاده از کنشهای سفارشی، دو تکضرب بزنید و نگه دارید."</string>
+ <string name="long_press_widget_to_add" msgid="3587712543577675817">"برای جابهجا کردن ابزاره، لمس کنید و نگه دارید."</string>
+ <string name="long_accessible_way_to_add" msgid="2733588281439571974">"برای جابهجا کردن ابزاره یا استفاده از کنشهای سفارشی، دو تکضرب بزنید و نگه دارید."</string>
<string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"گزینههای بیشتر"</string>
<string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"نمایش همه ابزارهها"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d عرض در %2$d طول"</string>
- <string name="widget_preview_context_description" msgid="9045841361655787574">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
- <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g>، %2$d عرض در %3$d ارتفاع"</string>
- <string name="add_item_request_drag_hint" msgid="8730547755622776606">"ابزارک را لمس کنید و نگه دارید تا بتوانید آن را در صفحه اصلی حرکت دهید"</string>
+ <string name="widget_preview_context_description" msgid="9045841361655787574">"ابزاره <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"ابزاره <xliff:g id="WIDGET_NAME">%1$s</xliff:g>، %2$d عرض در %3$d ارتفاع"</string>
+ <string name="add_item_request_drag_hint" msgid="8730547755622776606">"ابزاره را لمس کنید و نگه دارید تا بتوانید آن را در صفحه اصلی حرکت دهید"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"افزودن به صفحه اصلی"</string>
- <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g> به صفحه اصلی اضافه شد"</string>
+ <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ابزاره <xliff:g id="WIDGET_NAME">%1$s</xliff:g> به صفحه اصلی اضافه شد"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"پیشنهادها"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ضروریات"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"اخبار و مجله"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"سرگرمی"</string>
<string name="social_widget_recommendation_category_label" msgid="689147679536384717">"اجتماعی"</string>
<string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"پیشنهاداتی برای شما"</string>
- <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ابزارکهای <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> در سمت چپ، جستجو و گزینهها در سمت راست"</string>
- <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ابزارک}one{# ابزارک}other{# ابزارک}}"</string>
+ <string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ابزارههای <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> در سمت چپ، «جستجو» و گزینهها در سمت راست"</string>
+ <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ابزاره}one{# ابزاره}other{# ابزاره}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# میانبر}one{# میانبر}other{# میانبر}}"</string>
<string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>،<xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"ابزارکها"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ابزارهها"</string>
<string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"جستجو"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"پاک کردن نوشتار از چارگوش جستجو"</string>
- <string name="no_widgets_available" msgid="4337693382501046170">"ابزارک و میانبری دردسترس نیست"</string>
- <string name="no_search_results" msgid="3787956167293097509">"هیچ ابزارک یا میانبری پیدا نشد"</string>
- <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ابزارکهای شخصی"</string>
- <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"کار"</string>
+ <string name="no_widgets_available" msgid="4337693382501046170">"ابزاره و میانبری دردسترس نیست"</string>
+ <string name="no_search_results" msgid="3787956167293097509">"هیچ ابزاره یا میانبری پیدا نشد"</string>
+ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ابزارههای شخصی"</string>
+ <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ابزارههای کاری"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"مکالمهها"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"یادداشتبرداری"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"افزودن"</string>
- <string name="widget_add_button_content_description" msgid="1810530016360039643">"افزودن ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
- <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"برای تغییر تنظیمات ابزارک، تکضرب بزنید"</string>
- <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغییر تنظیمات ابزارک"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"افزودن ابزاره <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"برای تغییر تنظیمات ابزاره، تکضرب بزنید"</string>
+ <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغییر تنظیمات ابزاره"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"جستجوی برنامهها"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"درحال بارگیری برنامهها…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"هیچ برنامهای در مطابقت با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
@@ -92,16 +92,15 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"نصب"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"برنامه پیشنهاد داده نشود"</string>
<string name="pin_prediction" msgid="4196423321649756498">"سنجاق کردن پیشنهاد"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"حبابک"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میانبرها"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه میدهد میانبرها را بدون دخالت کاربر اضافه کند."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"خواندن تنظیمات و میانبرهای صفحه اصلی"</string>
<string name="permdesc_read_settings" msgid="4208061150510996676">"به برنامه اجازه میدهد تنظیمات و میانبرهای صفحه اصلی را بخواند."</string>
<string name="permlab_write_settings" msgid="4820028712156303762">"نوشتن تنظیمات و میانبرهای صفحه اصلی"</string>
<string name="permdesc_write_settings" msgid="726859348127868466">"به برنامه اجازه میدهد تنظیمات و میانبرهای صفحه اصلی را تغییر دهد."</string>
- <string name="gadget_error_text" msgid="740356548025791839">"ابزارک را نمیتوان بار کرد"</string>
- <string name="gadget_setup_text" msgid="8348374825537681407">"تنظیمات ابزارک"</string>
+ <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="folder_hint_text" msgid="5174843001373488816">"ویرایش نام"</string>
@@ -147,8 +146,8 @@
<string name="dialog_update_message" msgid="4176784553982226114">"برنامه برای این نماد بهروز نشده است. میتوانید آن را بهصورت دستی بهروز کنید تا میانبر دوباره فعال شود، یا نماد را بردارید."</string>
<string name="dialog_update" msgid="2178028071796141234">"بهروزرسانی"</string>
<string name="dialog_remove" msgid="6510806469849709407">"برداشتن"</string>
- <string name="widgets_list" msgid="796804551140113767">"فهرست ابزارکها"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"فهرست ابزارکها بسته شد"</string>
+ <string name="widgets_list" msgid="796804551140113767">"فهرست ابزارهها"</string>
+ <string name="widgets_list_closed" msgid="6141506579418771922">"فهرست ابزارهها بسته شد"</string>
<string name="action_add_to_workspace" msgid="215894119683164916">"افزودن به صفحه اصلی"</string>
<string name="action_move_here" msgid="2170188780612570250">"انتقال مورد به اینجا"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"مورد به صفحه اصلی اضافه شد"</string>
@@ -170,7 +169,7 @@
<string name="action_increase_height" msgid="459390020612501122">"افزایش ارتفاع"</string>
<string name="action_decrease_width" msgid="1374549771083094654">"کاهش عرض"</string>
<string name="action_decrease_height" msgid="282377193880900022">"کاهش ارتفاع"</string>
- <string name="widget_resized" msgid="9130327887929620">"اندازه ابزارک به عرض <xliff:g id="NUMBER_0">%1$s</xliff:g> ارتفاع <xliff:g id="NUMBER_1">%2$s</xliff:g> تغییر کرد"</string>
+ <string name="widget_resized" msgid="9130327887929620">"اندازه ابزاره به عرض <xliff:g id="NUMBER_0">%1$s</xliff:g> ارتفاع <xliff:g id="NUMBER_1">%2$s</xliff:g> تغییر کرد"</string>
<string name="action_deep_shortcut" msgid="2864038805849372848">"میانبرها"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"رد کردن"</string>
<string name="accessibility_close" msgid="2277148124685870734">"بستن"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 0cf15f4..007d077 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Asenna"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Älä ehdota sovellusta"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Kiinnitä sovellus"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Kupla"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lukea aloitusnäytön asetuksia ja pikakuvakkeita"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 447909a..c443505 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bulle"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une appli d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 48c3bde..4f5d111 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -31,7 +31,7 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Infos sur l\'appli pour %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Paramètres d\'utilisation pour %1$s"</string>
- <string name="save_app_pair" msgid="5647523853662686243">"Enregistrer la paire d\'applis"</string>
+ <string name="save_app_pair" msgid="5647523853662686243">"Enregistrer une paire d\'applis"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cette paire d\'applications n\'est pas prise en charge sur cet appareil"</string>
<string name="app_pair_needs_unfold" msgid="4588897528143807002">"Dépliez l\'appareil pour utiliser cette paire d\'applications"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bulle"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Lire les paramètres et les raccourcis de la page d\'accueil"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 61a7e3d..ff7c029 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar predición"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Burbulla"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ler a configuración e os atallos da pantalla de inicio"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index b85038c..872faef 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ઇન્સ્ટૉલ કરો"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ઍપ સૂચવશો નહીં"</string>
<string name="pin_prediction" msgid="4196423321649756498">"પૂર્વાનુમાનને પિન કરો"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"બબલ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"શૉર્ટકટ ઇન્સ્ટૉલ કરો"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"હોમ સેટિંગ અને શૉર્ટકટ વાંચો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 1205515..a44b874 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"इंस्टॉल करें"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ऐप्लिकेशन का सुझाव न दें"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सुझाए गए ऐप पिन करें"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"बबल"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करें"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप को उपयोगकर्ता के हस्तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रीन की सेटिंग और शॉर्टकट पढ़ने की अनुमति"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 1238172..d9f2072 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Oblačić"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čitati postavke i prečace početnog zaslona"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index acd4156..6bc8b70 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Telepítés"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne javasoljon appot"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Várható kitűzése"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Buborék"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"kezdőképernyő beállításainak és parancsikonjainak olvasása"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 2c020ad..69b320d 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Տեղադրել"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Չառաջարկել"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ամրացնել առաջարկվող հավելվածը"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Ամպիկ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Դյուրանցումների տեղադրում"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Հավելվածին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"կարդալ հիմնական էկրանի կարգավորումներն ու դյուրանցումները"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 6586bb4..58a429f 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instal"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan sarankan apl"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin Prediksi"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Balon"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"membaca setelan dan pintasan layar utama"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index f4593ec..95bd21f 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Setja upp"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ekki fá tillögu að forriti"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Festa tillögu"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Blaðra"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lesa stillingar og flýtileiðir heimaskjás"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a444d21..3c01cd4 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installa"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suggerire app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Blocca previsione"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Fumetto"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Aggiunta di scorciatoie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"leggere le impostazioni e le scorciatoie nella schermata Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 3a45b9f..f198166 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -63,7 +63,7 @@
<string name="no_widgets_available" msgid="4337693382501046170">"אין ווידג\'טים או קיצורי דרך"</string>
<string name="no_search_results" msgid="3787956167293097509">"לא נמצאו ווידג\'טים או קיצורי דרך"</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ווידג\'טים אישיים"</string>
- <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"עבודה"</string>
+ <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ווידג\'טים לעבודה"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"שיחות"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"כתיבת הערות"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"הוספה"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"התקנה"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"בלי להציע את האפליקציה"</string>
<string name="pin_prediction" msgid="4196423321649756498">"הצמדת החיזוי"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"בועה"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"התקנת קיצורי דרך"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"קריאת ההגדרות וקיצורי הדרך בדף הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 9dacea1..d2f9a97 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"インストール"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリを表示しない"</string>
<string name="pin_prediction" msgid="4196423321649756498">"アプリの候補を固定"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ふきだし"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ホームの設定とショートカットの読み取り"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 9e0e4a9..e67cc41 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ინსტალაცია"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"არ შემომთავაზო აპი"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ჩამაგრების პროგნოზირება"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ბუშტი"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 48f3478..d5ccae5 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Орнату"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Қолданба ұсынбау"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Болжамды бекіту"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Қалқыма терезе"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"таңбаша орнату"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"негізгі экран параметрлері мен таңбашаларын оқу"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 0044e6a..3c9135b 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ដំឡើង"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"កុំណែនាំកម្មវិធី"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ខ្ទាស់ការព្យាករ"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ពពុះ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើងផ្លូវកាត់"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាតឲ្យកម្មវិធីបន្ថែមផ្លូវកាត់ ដោយមិនចាំបាច់អំពើពីអ្នកប្រើ។"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"អានការកំណត់ និងផ្លូវកាត់របស់អេក្រង់ដើម"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index f2d4e55..ab84833 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ಸ್ಥಾಪಿಸಿ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ಆ್ಯಪ್ ಅನ್ನು ಸೂಚಿಸಬೇಡಿ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ಮುನ್ನೋಟ ಪಿನ್ ಮಾಡಿ"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ಬಬಲ್"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಓದಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index f3e5a3a..318cd00 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"설치"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"앱 제안 받지 않음"</string>
<string name="pin_prediction" msgid="4196423321649756498">"예상 앱 고정"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"풍선"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"홈 설정 및 바로가기 읽기"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index e955666..856e2b2 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -39,7 +39,7 @@
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Виджетти кое бербей басып туруп жылдырыңыз."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Виджетти жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
<string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Дагы параметрлер"</string>
- <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Бардык виджет-ди көрсөтүү"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Виджеттин баарын көрсөтүү"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджети"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Орнотуу"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Cунушталбасын"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Божомолдонгон колдонмону кадап коюу"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Көбүкчө"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"үйдүн параметрлерин жана ыкчам баскычтарын окуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 78fe222..3d1a6c9 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ຕິດຕັ້ງ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ຢ່າແນະນຳແອັບ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ປັກໝຸດການຄາດເດົາ"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ຟອງ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະ ທາງລັດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4954848..4c9bd9b 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Įdiegti"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nesiūlyti programos"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Prisegti numatymą"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Debesėlis"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"skaityti pagrindinio ekrano nustatymus ir sparčiuosius klavišus"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 49e9825..0a82705 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalēt"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Neieteikt lietotni"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Piespraust prognozēto lietotni"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Burbulis"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"sākuma ekrāna iestatījumu un saīšņu lasīšana"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 626a80e..887ca82 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Инсталирај"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлагај апл."</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закачи го предвидувањето"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Балонче"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање кратенки"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"да чита поставки и кратенки на почетна страница"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index b777e0f..dda5679 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ആപ്പ് നിർദ്ദേശിക്കേണ്ട"</string>
<string name="pin_prediction" msgid="4196423321649756498">"പ്രവചനം പിൻ ചെയ്യുക"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ബബിൾ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ഹോം ക്രമീകരണവും കുറുക്കുവഴികളും വായിക്കുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 6fe7967..49d71c2 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Суулгах"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Апп бүү санал болго"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Таамаглалыг бэхлэх"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Бөмбөлөг"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"нүүрний тохиргоо болон товчлолыг унших"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 22d789b..fdf864a 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"इंस्टॉल करा"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ॲप सुचवू नका"</string>
<string name="pin_prediction" msgid="4196423321649756498">"पूर्वानुमान पिन करा"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"बबल"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्टॉल करा"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"होम सेटिंग्ज आणि शॉर्टकट वाचा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 9320e45..b86f657 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -63,7 +63,7 @@
<string name="no_widgets_available" msgid="4337693382501046170">"Widget dan pintasan tidak tersedia"</string>
<string name="no_search_results" msgid="3787956167293097509">"Tiada widget atau pintasan yang dijumpai"</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Peribadi"</string>
- <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Tempat kerja"</string>
+ <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kerja"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Perbualan"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pengambilan nota"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Tambah"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Pasang"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan cadangkan apl"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Sematkan Ramalan"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Gelembung"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"membaca tetapan dan pintasan skrin utama"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 599c9a1..7e8fd14 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ထည့်သွင်းရန်"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်အကြံမပြုပါနှင့်"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ခန့်မှန်းချက်ကို ပင်ထိုးရန်"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ပူဖောင်းကွက်"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ဖြတ်လမ်းလင့်ခ်များ ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ပင်မဆက်တင်နှင့် ဖြတ်လမ်းလင့်ခ်များ ဖတ်ခြင်း"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 11b076c..2a6611f 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ikke foreslå app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fest forslaget"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Boble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lese startsideinnstillinger og -snarveier"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index add65ee..fa2e59b 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"एप सिफारिस नगर्नुहोस्"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"बबल"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"होम स्क्रिनका सेटिङ र सर्टकटहरू रिड गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index c2bc5e0..9271b96 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installeren"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Geen app voorstellen"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Voorspelling vastzetten"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubbel"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"instellingen en snelkoppelingen op startscherm lezen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 01c6dfc..98d52de 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ବବଲ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ସର୍ଟକଟ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ୟୁଜରଙ୍କ ବିନା ହସ୍ତକ୍ଷେପରେ ଶର୍ଟକଟ୍ ଯୋଡ଼ିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ହୋମ ସେଟିଂସ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକୁ ପଢ଼ନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index dce05ae..782979e 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -63,7 +63,7 @@
<string name="no_widgets_available" msgid="4337693382501046170">"ਵਿਜੇਟ ਜਾਂ ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹਨ"</string>
<string name="no_search_results" msgid="3787956167293097509">"ਕੋਈ ਵੀ ਵਿਜੇਟ ਜਾਂ ਸ਼ਾਰਟਕੱਟ ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ਨਿੱਜੀ"</string>
- <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ਕਾਰਜ-ਸਥਾਨ"</string>
+ <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ਕੰਮ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ਗੱਲਾਂਬਾਤਾਂ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ਨੋਟ ਬਣਾਉਣਾ"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ਸਥਾਪਤ ਕਰੋ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ਐਪ ਦਾ ਸੁਝਾਅ ਨਾ ਦਿਓ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ਪੂਰਵ-ਅਨੁਮਾਨ ਪਿੰਨ ਕਰੋ"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"ਬਬਲ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਵਰਤੋਂਕਾਰ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index fae07369..71e569c 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Zainstaluj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nie proponuj aplikacji"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Przypnij podpowiedź"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Dymek"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalowanie skrótów"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Odczytuje ustawienia i skróty na ekranie głównym"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 69b63cf..1c44d9b 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Balão"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma app adicionar atalhos sem a intervenção do utilizador."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ler definições e atalhos do ecrã Principal"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f4ee373..3f44591 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir esse app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Balão"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ler configurações e atalhos da tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 5293657..b37d93b 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalează"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nu sugera aplicația"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixează predicția"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Balon"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"citește setările și comenzile rapide de pe ecranul de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 0e8faa1..321bf37 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -47,7 +47,7 @@
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Нажмите на виджет и удерживайте его, чтобы переместить в нужное место на главном экране."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Добавить на главный экран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" добавлен на главный экран"</string>
- <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Подсказки"</string>
+ <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Рекомендованные"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основное"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и журналы"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Развлечения"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Установить"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не рекомендовать"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закрепить рекомендацию"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Подсказка"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Доступ к данным о настройках и ярлыках на главном экране"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 9db82a7..91c818a 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ස්ථාපනය කරන්න"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"යෙදුම යෝජනා නොකරන්න"</string>
<string name="pin_prediction" msgid="4196423321649756498">"පුරෝකථනය අමුණන්න"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"බුබුළ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"මුල් පිටු සැකසීම් සහ කෙටි මං කියවන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 5007a7f..eaae7c0 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -39,7 +39,7 @@
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Pridržaním presuňte miniaplikáciu."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvojitým klepnutím a pridržaním presuňte miniaplikáciu alebo použite vlastné akcie."</string>
<string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Ďalšie možnosti"</string>
- <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Zobrazovať všetky miniap."</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Zobrazovať všetky miniaplikácie"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Miniaplikácia <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
@@ -58,12 +58,12 @@
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# odkaz}few{# odkazy}many{# shortcuts}other{# odkazov}}"</string>
<string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
- <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Vyhľadajte"</string>
+ <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Vyhľadávanie"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Vymazať text z vyhľadávacieho poľa"</string>
<string name="no_widgets_available" msgid="4337693382501046170">"Miniaplikácie a odkazy nie sú k dispozícii"</string>
<string name="no_search_results" msgid="3787956167293097509">"Nenašli sa žiadne miniaplikácie ani odkazy"</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobné"</string>
- <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práca"</string>
+ <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Pracovné"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Konverzácie"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Zapisovanie poznámok"</string>
<string name="widget_add_button_label" msgid="2761267068711937179">"Pridať"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Inštalovať"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovať aplikáciu"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pripnúť predpoveď"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bublina"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalácia odkazov"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"čítanie nastavení a odkazov plochy"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index ab2eff3..dccf2f1 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Namesti"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlagaj"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Predvidevanje pripenjanja"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Mehurček"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index a61cd6d..84484e8 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalo"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Mos sugjero aplikacion"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Gozhdo parashikimin"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Flluskë"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalimi i shkurtoreve"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 621046f..64383f2 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Инсталирај"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлажи апликацију"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закачи предвиђање"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Облачић"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"читање подешавања и пречица на почетном екрану"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 812cbd7..47aed86 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -62,7 +62,7 @@
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Rensa texten från sökrutan"</string>
<string name="no_widgets_available" msgid="4337693382501046170">"Widgetar och genvägar är inte tillgängliga"</string>
<string name="no_search_results" msgid="3787956167293097509">"Inga widgetar eller genvägar hittades"</string>
- <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privata"</string>
+ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privat"</string>
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbete"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Konversationer"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Anteckna"</string>
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Installera"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Föreslå inte app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fäst förslag"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubbla"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"läsa inställningar och genvägar på startskärmen"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 5464af3..5eadd1d 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Sakinisha"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Isipendekeze programu"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Bandika Utabiri"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Kiputo"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"kusoma mipangilio ya skrini ya kwanza na njia za mkato"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 29ca948..d84485a 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"நிறுவு"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"பரிந்துரைக்காதே"</string>
<string name="pin_prediction" msgid="4196423321649756498">"கணிக்கப்பட்டதைப் பின் செய்"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"குமிழ்"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"முகப்புத் திரையின் அமைப்புகளையும் ஷார்ட்கட்களையும் படித்தல்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 038f6b7..8487e96 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ఇన్స్టాల్ చేయండి"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ సూచించకు"</string>
<string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"బబుల్"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్కట్లను ఇన్స్టాల్ చేయడం"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా షార్ట్కట్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"హోమ్ సెట్టింగ్లు, షార్ట్కట్లను చదవండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 72684cd..71f4d15 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"ติดตั้ง"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ไม่ต้องแนะนำแอป"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ปักหมุดแอปที่คาดการณ์ไว้"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"บับเบิล"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"อ่านการตั้งค่าและทางลัดในหน้าแรก"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index ec265c7..7cf6a44 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"I-install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Huwag magmungkahi"</string>
<string name="pin_prediction" msgid="4196423321649756498">"I-pin ang Hula"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bubble"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"basahin ang mga setting at shortcut ng home"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index dd4dab6..d55181c 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Yükle"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulamayı önerme"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Tahmini Sabitle"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Balon"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ana ekran ayarlarını ve kısayollarını oku"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 7048517..b5c1c70 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Установити"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не пропонувати додаток"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закріпити передбачений додаток"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Повідомлення"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"читати налаштування та ярлики головного екрана"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 8460f73..6fa76dd 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"انسٹال کریں"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ایپ تجویز نہ کریں"</string>
<string name="pin_prediction" msgid="4196423321649756498">"پیشگوئی پن کریں"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"بلبلہ"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index be02e01..21a8145 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"O‘rnatish"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Tavsiya qilinmasin"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Tavsiyani mahkamlash"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Pufaklar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"Bosh ekrandagi sozlamalar va yorliqlarni koʻrish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 3d8b815..b0bac73 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Cài đặt"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Không gợi ý ứng dụng"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ghim ứng dụng dự đoán"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Bong bóng"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"đọc lối tắt và các chế độ cài đặt trên màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 12a3a70..112b945 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"安装"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要推荐此应用"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定预测的应用"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"气泡框"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"读取主屏幕设置和快捷方式"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index c82a320..e63093e 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"安裝"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供應用程式建議"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定預測"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"氣泡"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 714fc24..25f9703 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"安裝"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要建議此應用程式"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定預測的應用程式"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"泡泡"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"讀取主畫面設定和捷徑"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index a84b1fb..ec1f941 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -92,8 +92,7 @@
<string name="install_drop_target_label" msgid="2539096853673231757">"Faka"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
- <!-- no translation found for bubble (3072951361014076670) -->
- <skip />
+ <string name="bubble" msgid="3072951361014076670">"Ibhamuza"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
<string name="permlab_read_settings" msgid="5136500343007704955">"funda amasethingi wasekhaya nezinqamuleli"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5e1d8a5..f8c075f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,6 +81,11 @@
<dimen name="fastscroll_popup_text_size">32dp</dimen>
<dimen name="fastscroll_popup_margin">19dp</dimen>
+ <dimen name="fastscroll_list_letter_size">5dp</dimen>
+ <dimen name="fastscroll_list_letter_text_size">14sp</dimen>
+ <dimen name="fastscroll_list_letter_end_margin">-10dp</dimen>
+ <dimen name="bg_letter_list_text_size">20sp</dimen>
+
<!--
Fast scroller draws the content horizontally centered. The end of the track should be
aligned at the end of the container.
@@ -306,7 +311,7 @@
<dimen name="blur_size_medium_outline">2dp</dimen>
<dimen name="blur_size_click_shadow">4dp</dimen>
<dimen name="click_shadow_high_shift">2dp</dimen>
- <dimen name="app_title_icon_shadow_inset">1dp</dimen>
+ <dimen name="app_title_icon_shadow_inset">0.5dp</dimen>
<!-- Pending widget -->
<dimen name="pending_widget_min_padding">8dp</dimen>
@@ -456,6 +461,7 @@
<!-- Overview placeholder to compile in Launcher3 without Quickstep -->
<dimen name="task_thumbnail_icon_size">0dp</dimen>
<dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
+ <dimen name="task_thumbnail_splash_icon_size">0dp</dimen>
<dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
<dimen name="task_thumbnail_icon_menu_drawable_touch_size">0dp</dimen>
<dimen name="task_menu_edge_padding">0dp</dimen>
@@ -482,8 +488,6 @@
<dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
<dimen name="split_instructions_start_margin_cancel">8dp</dimen>
- <dimen name="split_divider_handle_region_width">96dp</dimen>
- <dimen name="split_divider_handle_region_height">48dp</dimen>
<dimen name="focus_outline_radius">16dp</dimen>
<dimen name="focus_inner_outline_radius">14dp</dimen>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1eccbff..26e900d 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -176,7 +176,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mSkipUserBadge = false;
@ViewDebug.ExportedProperty(category = "launcher")
- private boolean mIsIconVisible = true;
+ protected boolean mIsIconVisible = true;
@ViewDebug.ExportedProperty(category = "launcher")
private int mTextColor;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -1043,12 +1043,11 @@
/** Applies the given progress level to the this icon's progress bar. */
@Nullable
public PreloadIconDrawable applyProgressLevel() {
- if (!(getTag() instanceof ItemInfoWithIcon)
+ if (!(getTag() instanceof ItemInfoWithIcon info)
|| ((ItemInfoWithIcon) getTag()).isInactiveArchive()) {
return null;
}
- ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
int progressLevel = info.getProgressLevel();
if (progressLevel >= 100) {
setContentDescription(info.contentDescription != null
@@ -1068,6 +1067,10 @@
} else {
preloadIconDrawable = makePreloadIcon();
setIcon(preloadIconDrawable);
+ if (info.isArchived() && Flags.useNewIconForArchivedApps()) {
+ // reapply text without cloud icon as soon as unarchiving is triggered
+ applyLabel(info);
+ }
}
return preloadIconDrawable;
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 7e9e864..ee72c22 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -86,7 +86,7 @@
public class CellLayout extends ViewGroup {
private static final String TAG = "CellLayout";
- private static final boolean LOGD = false;
+ private static final boolean LOGD = true;
/** The color of the "leave-behind" shape when a folder is opened from Hotseat. */
private static final int FOLDER_LEAVE_BEHIND_COLOR = Color.argb(160, 245, 245, 245);
@@ -166,6 +166,7 @@
private final int[] mDragCellSpan = new int[2];
private boolean mDragging = false;
+ public boolean mHasOnLayoutBeenCalled = false;
private final TimeInterpolator mEaseOutInterpolator;
protected final ShortcutAndWidgetContainer mShortcutsAndWidgets;
@@ -1009,6 +1010,7 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mHasOnLayoutBeenCalled = true; // b/349929393 - is the required call to onLayout not done?
int left = getPaddingLeft();
left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
int right = r - l - getPaddingRight();
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index de1748b..6622e11 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -25,6 +25,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -54,9 +55,11 @@
super(context, attrs, defStyleAttr);
}
- public void bindFastScrollbar(RecyclerViewFastScroller scrollbar) {
+ public void bindFastScrollbar(RecyclerViewFastScroller scrollbar,
+ RecyclerViewFastScroller.FastScrollerLocation location) {
mScrollbar = scrollbar;
mScrollbar.setRecyclerView(this);
+ mScrollbar.setFastScrollerLocation(location);
onUpdateScrollbar(0);
}
@@ -163,6 +166,13 @@
public abstract void onUpdateScrollbar(int dy);
/**
+ * Return the fast scroll letter list view in the A-Z list.
+ */
+ public ConstraintLayout getLetterList() {
+ return null;
+ }
+
+ /**
* <p>Override in each subclass of this base class.
*/
public void onFastScrollCompleted() {}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fac372b..3ca6099 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -135,14 +135,12 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
-import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
@@ -214,7 +212,6 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.popup.PopupDataProvider;
@@ -1410,15 +1407,6 @@
this, R.attr.isWorkspaceDarkText) ? Color.BLACK : Color.WHITE);
}
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- if (WorkspacePageIndicator.class.getName().equals(name)) {
- return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
- (ViewGroup) parent, false);
- }
- return super.onCreateView(parent, name, context, attrs);
- }
-
/**
* Add a shortcut to the workspace or to a Folder.
*
@@ -2701,6 +2689,20 @@
writer.println(prefix + "\tmAppWidgetHolder.isListening: "
+ mAppWidgetHolder.isListening());
+ // b/349929393
+ // The only way to reproduce this bug is to ensure that onLayout never gets called. This
+ // theoretically is impossible, so these logs are being added to test if that actually is
+ // what is happening.
+ writer.println(prefix + "\tmWorkspace.mHasOnLayoutBeenCalled="
+ + mWorkspace.mHasOnLayoutBeenCalled);
+ for (int i = 0; i < mWorkspace.getPageCount(); i++) {
+ CellLayout cellLayout = (CellLayout) mWorkspace.getPageAt(i);
+ writer.println(prefix + "\tcellLayout." + i + ".mHasOnLayoutBeenCalled="
+ + cellLayout.mHasOnLayoutBeenCalled);
+ writer.println(prefix + "\tshortcutAndWidgetContainer." + i + ".mHasOnLayoutBeenCalled="
+ + cellLayout.getShortcutsAndWidgets().mHasOnLayoutBeenCalled);
+ }
+
// Extra logging for general debugging
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index d2c3c78..a8733f2 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -64,6 +64,7 @@
private final ActivityContext mActivity;
private boolean mInvertIfRtl = false;
+ public boolean mHasOnLayoutBeenCalled = false;
@Nullable
private TranslationProvider mTranslationProvider = null;
@@ -201,6 +202,7 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Trace.beginSection("ShortcutAndWidgetConteiner#onLayout");
+ mHasOnLayoutBeenCalled = true; // b/349929393 - is the required call to onLayout not done?
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2995e8a..255260e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -235,6 +235,7 @@
boolean mChildrenLayersEnabled = true;
private boolean mStripScreensOnPageStopMoving = false;
+ public boolean mHasOnLayoutBeenCalled = false;
private boolean mWorkspaceFadeInAdjacentScreens;
@@ -1445,6 +1446,7 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mHasOnLayoutBeenCalled = true; // b/349929393 - is the required call to onLayout not done?
if (mUnlockWallpaperFromDefaultPageOnLayout) {
mWallpaperOffset.setLockToDefaultPage(false);
mUnlockWallpaperFromDefaultPageOnLayout = false;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 227ac2b..cc4724c 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -29,6 +29,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
+import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.ALL_APPS_SCROLLER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -65,6 +66,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.VisibleForTesting;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
@@ -168,6 +170,7 @@
protected FloatingHeaderView mHeader;
protected View mBottomSheetBackground;
protected RecyclerViewFastScroller mFastScroller;
+ private ConstraintLayout mFastScrollLetterLayout;
/**
* View that defines the search box. Result is rendered inside {@link #mSearchRecyclerView}.
@@ -282,6 +285,13 @@
mSearchRecyclerView = findViewById(R.id.search_results_list_view);
mFastScroller = findViewById(R.id.fast_scroller);
mFastScroller.setPopupView(findViewById(R.id.fast_scroller_popup));
+ mFastScrollLetterLayout = findViewById(R.id.scroll_letter_layout);
+ if (Flags.letterFastScroller()) {
+ // Set clip children to false otherwise the scroller letters will be clipped.
+ setClipChildren(false);
+ } else {
+ setClipChildren(true);
+ }
mSearchContainer = inflateSearchBar();
if (!isSearchBarFloating()) {
@@ -563,7 +573,8 @@
mActivityContext.hideKeyboard();
}
if (mAH.get(currentActivePage).mRecyclerView != null) {
- mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller);
+ mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller,
+ ALL_APPS_SCROLLER);
}
// Header keeps track of active recycler view to properly render header protection.
mHeader.setActiveRV(currentActivePage);
@@ -1500,6 +1511,10 @@
}
}
+ ConstraintLayout getFastScrollerLetterList() {
+ return mFastScrollLetterLayout;
+ }
+
/**
* redraws header protection
*/
@@ -1567,7 +1582,7 @@
void setup(@NonNull View rv, @Nullable Predicate<ItemInfo> matcher) {
mAppsList.updateItemFilter(matcher);
mRecyclerView = (AllAppsRecyclerView) rv;
- mRecyclerView.bindFastScrollbar(mFastScroller);
+ mRecyclerView.bindFastScrollbar(mFastScroller, ALL_APPS_SCROLLER);
mRecyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
mRecyclerView.setApps(mAppsList);
mRecyclerView.setLayoutManager(mLayoutManager);
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 2a47222..ae45a35 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.allapps;
+import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
+import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+
import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
@@ -36,22 +39,29 @@
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.util.Consumer;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.FastScrollRecyclerView;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.views.ActivityContext;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -66,6 +76,7 @@
protected final int mNumAppsPerRow;
private final AllAppsFastScrollHelper mFastScrollHelper;
private int mCumulativeVerticalScroll;
+ private ConstraintLayout mLetterList;
protected AlphabeticalAppsList<?> mApps;
@@ -238,6 +249,9 @@
return;
}
+ if (Flags.letterFastScroller() && !mScrollbar.isDraggingThumb()) {
+ setLettersToScrollLayout(mApps.getFastScrollerSections());
+ }
// Only show the scrollbar if there is height to be scrolled
int availableScrollBarHeight = getAvailableScrollBarHeight();
int availableScrollHeight = getAvailableScrollHeight();
@@ -319,6 +333,80 @@
return false;
}
+ public void setLettersToScrollLayout(
+ List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections) {
+ if (mLetterList != null) {
+ mLetterList.removeAllViews();
+ }
+ Context context = getContext();
+ ActivityAllAppsContainerView<?> allAppsContainerView =
+ ActivityContext.lookupContext(context).getAppsView();
+ mLetterList = allAppsContainerView.getFastScrollerLetterList();
+ mLetterList.setPadding(0, getScrollBarTop(), 0, getScrollBarMarginBottom());
+ List<LetterListTextView> textViews = new ArrayList<>();
+ for (int i = 0; i < fastScrollSections.size(); i++) {
+ AlphabeticalAppsList.FastScrollSectionInfo sectionInfo = fastScrollSections.get(i);
+ LetterListTextView textView =
+ (LetterListTextView) LayoutInflater.from(context).inflate(
+ R.layout.fast_scroller_letter_list_text_view, mLetterList, false);
+ int viewId = View.generateViewId();
+ textView.setId(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);
+ }
+ // Need to add an extra textview to be aligned.
+ LetterListTextView lastLetterListTextView = new LetterListTextView(context);
+ int currentId = View.generateViewId();
+ lastLetterListTextView.setId(currentId);
+ lastLetterListTextView.setVisibility(INVISIBLE);
+ textViews.add(lastLetterListTextView);
+ mLetterList.addView(lastLetterListTextView);
+ constraintTextViewsVertically(mLetterList, textViews);
+ mLetterList.setVisibility(VISIBLE);
+ }
+
+ private void constraintTextViewsVertically(ConstraintLayout constraintLayout,
+ List<LetterListTextView> textViews) {
+ ConstraintSet chain = new ConstraintSet();
+ chain.clone(constraintLayout);
+ for (int i = 0; i < textViews.size(); i++) {
+ LetterListTextView currentView = textViews.get(i);
+ if (i == 0) {
+ chain.connect(currentView.getId(), ConstraintSet.TOP, ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP);
+ } else {
+ chain.connect(currentView.getId(), ConstraintSet.TOP, textViews.get(i-1).getId(),
+ ConstraintSet.BOTTOM);
+ }
+ chain.connect(currentView.getId(), ConstraintSet.START, constraintLayout.getId(),
+ ConstraintSet.START);
+ chain.connect(currentView.getId(), ConstraintSet.END, constraintLayout.getId(),
+ ConstraintSet.END);
+ }
+ int[] viewIds = textViews.stream().mapToInt(TextView::getId).toArray();
+ float[] weights = new float[textViews.size()];
+ Arrays.fill(weights,1); // fill with 1 for equal weights
+ chain.createVerticalChain(constraintLayout.getId(), ConstraintSet.TOP,
+ constraintLayout.getId(), ConstraintSet.BOTTOM, viewIds, weights,
+ ConstraintSet.CHAIN_SPREAD);
+ chain.applyTo(constraintLayout);
+ }
+
+ @Override
+ public ConstraintLayout getLetterList() {
+ return mLetterList;
+ }
+
private void logCumulativeVerticalScroll() {
ActivityContext context = ActivityContext.lookupContext(getContext());
StatsLogManager mgr = context.getStatsLogManager();
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 6dd811a..8e44d65 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -74,11 +74,17 @@
public final CharSequence sectionName;
// The item position
public final int position;
+ // The view id associated with this section
+ public int id = -1;
public FastScrollSectionInfo(CharSequence sectionName, int position) {
this.sectionName = sectionName;
this.position = position;
}
+
+ public void setId(int id) {
+ this.id = id;
+ }
}
diff --git a/src/com/android/launcher3/allapps/LetterListTextView.java b/src/com/android/launcher3/allapps/LetterListTextView.java
new file mode 100644
index 0000000..9326d79
--- /dev/null
+++ b/src/com/android/launcher3/allapps/LetterListTextView.java
@@ -0,0 +1,133 @@
+/*
+ * 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.allapps;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import androidx.core.graphics.ColorUtils;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Themes;
+
+/**
+ * A TextView that is used to display the letter list in the fast scroller.
+ */
+public class LetterListTextView extends TextView {
+ private static final float ABSOLUTE_TRANSLATION_X = 30f;
+ private static final float ABSOLUTE_SCALE = 1.4f;
+ private final Drawable mLetterBackground;
+ private final int mLetterListTextWidthAndHeight;
+ private final int mTextColor;
+ private final int mBackgroundColor;
+ private final int mSelectedColor;
+
+ public LetterListTextView(Context context) {
+ this(context, null, 0);
+ }
+
+ public LetterListTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LetterListTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mLetterBackground = context.getDrawable(R.drawable.bg_letter_list_text);
+ mLetterListTextWidthAndHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.fastscroll_list_letter_size);
+ mTextColor = Themes.getAttrColor(context, R.attr.materialColorOnSurface);
+ mBackgroundColor = Themes.getAttrColor(context, R.attr.materialColorSurfaceContainer);
+ mSelectedColor = Themes.getAttrColor(context, R.attr.materialColorOnSecondary);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ setBackground(mLetterBackground);
+ setTextColor(mTextColor);
+ setClickable(false);
+ setWidth(mLetterListTextWidthAndHeight);
+ setTextSize(mLetterListTextWidthAndHeight);
+ setVisibility(VISIBLE);
+ }
+
+ /**
+ * 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
+ * pixels.
+ */
+ public void animateBasedOnYPosition(int currentFingerY) {
+ if (getBackground() == null) {
+ return;
+ }
+ float cutOffMin = currentFingerY - (getHeight() * 2);
+ float cutOffMax = currentFingerY + (getHeight() * 2);
+ float cutOffDistance = cutOffMax - cutOffMin;
+ // Update the background blend color
+ boolean isWithinAnimationBounds = getY() < cutOffMax && getY() > cutOffMin;
+ if (isWithinAnimationBounds) {
+ getBackground().setColorFilter(new PorterDuffColorFilter(
+ getBlendColorBasedOnYPosition(currentFingerY, cutOffDistance),
+ PorterDuff.Mode.MULTIPLY));
+ } else {
+ getBackground().setColorFilter(new PorterDuffColorFilter(
+ mBackgroundColor, PorterDuff.Mode.MULTIPLY));
+ }
+ translateBasedOnYPosition(currentFingerY, cutOffDistance, isWithinAnimationBounds);
+ scaleBasedOnYPosition(currentFingerY, cutOffDistance, isWithinAnimationBounds);
+ }
+
+ private int getBlendColorBasedOnYPosition(int y, float cutOffDistance) {
+ float raisedCosineBlend = (float) Math.cos(((y - getY()) / (cutOffDistance)) * Math.PI);
+ float blendRatio = Utilities.boundToRange(raisedCosineBlend, 0f, 1f);
+ return ColorUtils.blendARGB(mBackgroundColor, mSelectedColor, blendRatio);
+ }
+
+ private void scaleBasedOnYPosition(int y, float cutOffDistance,
+ boolean isWithinAnimationBounds) {
+ float raisedCosineScale = (float) Math.cos(((y - getY()) / (cutOffDistance)) * Math.PI)
+ * ABSOLUTE_SCALE;
+ if (isWithinAnimationBounds) {
+ raisedCosineScale = Utilities.boundToRange(raisedCosineScale, 1f, ABSOLUTE_SCALE);
+ setScaleX(raisedCosineScale);
+ setScaleY(raisedCosineScale);
+ } else {
+ setScaleX(1);
+ setScaleY(1);
+ }
+ }
+
+ private void translateBasedOnYPosition(int y, float cutOffDistance,
+ boolean isWithinAnimationBounds) {
+ float raisedCosineTranslation =
+ (float) Math.cos(((y - getY()) / (cutOffDistance)) * Math.PI)
+ * ABSOLUTE_TRANSLATION_X;
+ if (isWithinAnimationBounds) {
+ raisedCosineTranslation = -1 * Utilities.boundToRange(raisedCosineTranslation,
+ 0, ABSOLUTE_TRANSLATION_X);
+ setTranslationX(raisedCosineTranslation);
+ } else {
+ setTranslationX(0);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt b/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt
index 650df5a..e218b4d 100644
--- a/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt
+++ b/src/com/android/launcher3/debug/TestEventsEmitterProduction.kt
@@ -17,6 +17,7 @@
package com.android.launcher3.debug
import android.content.Context
+import android.util.Log
import com.android.launcher3.util.MainThreadInitializedObject
import com.android.launcher3.util.SafeCloseable
@@ -50,5 +51,7 @@
override fun close() {}
- override fun sendEvent(event: TestEvent) {}
+ override fun sendEvent(event: TestEvent) {
+ Log.d("TestEventsEmitterProduction", "Event sent ${event.event}")
+ }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 3edf1f2..7bec768 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -773,6 +773,7 @@
addAnimationStartListeners(anim);
// Because t=0 has the folder match the folder icon, we can skip the
// first frame and have the same movement one frame earlier.
+ Log.d("b/311077782", "Folder.animateOpen");
anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
anim.start();
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 406f697..de2269c 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -18,10 +18,12 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.LauncherActivityInfo;
+import android.os.Build;
import android.os.UserHandle;
import androidx.annotation.NonNull;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.icons.BaseIconFactory.IconOptions;
@@ -64,9 +66,16 @@
@Override
public BitmapInfo loadIcon(@NonNull Context context, @NonNull LauncherActivityInfo object) {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
- return li.createBadgedIconBitmap(LauncherAppState.getInstance(context)
- .getIconProvider().getIcon(object, li.mFillResIconDpi),
- new IconOptions().setUser(object.getUser()));
+ IconOptions iconOptions = new IconOptions().setUser(object.getUser());
+ iconOptions.mIsArchived = Flags.useNewIconForArchivedApps()
+ && Build.VERSION.SDK_INT >= 35
+ && object.getActivityInfo().isArchived;
+ return li.createBadgedIconBitmap(
+ LauncherAppState.getInstance(context)
+ .getIconProvider()
+ .getIcon(object, li.mFillResIconDpi),
+ iconOptions
+ );
}
}
}
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 551c2d8..59d1d00 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -192,22 +192,18 @@
}
private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
- final Exception stackTrace = new Exception();
// Queue the item up for adding if launcher has not loaded properly yet
MODEL_EXECUTOR.post(() -> {
Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
if (itemInfo == null) {
FileLog.d(LOG,
- "Adding PendingInstallShortcutInfo with no attached info to queue.",
- stackTrace);
+ "Adding PendingInstallShortcutInfo with no attached info to queue.");
} else {
FileLog.d(LOG,
- "Adding PendingInstallShortcutInfo to queue. Attached info: "
- + itemInfo.first,
- stackTrace);
+ "Adding PendingInstallShortcutInfo to queue."
+ + " Attached info: " + itemInfo.first);
}
-
addToQueue(info);
});
flushInstallQueue();
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 269cb9f..605accf 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -209,7 +209,10 @@
mApp.getContext().getContentResolver(),
"launcher_broadcast_installed_apps",
/* def= */ 0);
- if (launcherBroadcastInstalledApps == 1 && mIsRestoreFromBackup) {
+ boolean shouldAttachArchivingExtras = mIsRestoreFromBackup
+ && (launcherBroadcastInstalledApps == 1
+ || Flags.enableFirstScreenBroadcastArchivingExtras());
+ if (shouldAttachArchivingExtras) {
List<FirstScreenBroadcastModel> broadcastModels =
FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
mPmHelper,
diff --git a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
index b12b2bc..2ee5b80 100644
--- a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
+++ b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
@@ -38,6 +38,7 @@
LauncherApps.Callback() {
override fun onPackageAdded(packageName: String, user: UserHandle) {
+ FileLog.d(TAG, "onPackageAdded triggered for packageName=$packageName, user=$user")
taskExecutor.accept(PackageUpdatedTask(OP_ADD, user, packageName))
}
@@ -54,7 +55,7 @@
}
override fun onPackageRemoved(packageName: String, user: UserHandle) {
- FileLog.d(TAG, "package removed received $packageName")
+ FileLog.d(TAG, "onPackageRemoved triggered for packageName=$packageName, user=$user")
taskExecutor.accept(PackageUpdatedTask(OP_REMOVE, user, packageName))
}
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
deleted file mode 100644
index bde4e52..0000000
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ /dev/null
@@ -1,265 +0,0 @@
-package com.android.launcher3.pageindicators;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Themes;
-
-/**
- * A PageIndicator that briefly shows a fraction of a line when moving between pages
- *
- * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
- */
-public class WorkspacePageIndicator extends View implements Insettable, PageIndicator {
-
- private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
- private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
- public static final int WHITE_ALPHA = (int) (0.70f * 255);
- public static final int BLACK_ALPHA = (int) (0.65f * 255);
-
- private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
- private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
- private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
- private static final int ANIMATOR_COUNT = 3;
-
- private ValueAnimator[] mAnimators = new ValueAnimator[ANIMATOR_COUNT];
-
- private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
- private final Launcher mLauncher;
-
- private boolean mShouldAutoHide = true;
-
- // The alpha of the line when it is showing.
- private int mActiveAlpha = 0;
- // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
- private int mToAlpha;
- // A float value representing the number of pages, to allow for an animation when it changes.
- private float mNumPagesFloat;
- private int mCurrentScroll;
- private int mTotalScroll;
- private Paint mLinePaint;
- private final int mLineHeight;
-
- private static final Property<WorkspacePageIndicator, Integer> PAINT_ALPHA
- = new Property<WorkspacePageIndicator, Integer>(Integer.class, "paint_alpha") {
- @Override
- public Integer get(WorkspacePageIndicator obj) {
- return obj.mLinePaint.getAlpha();
- }
-
- @Override
- public void set(WorkspacePageIndicator obj, Integer alpha) {
- obj.mLinePaint.setAlpha(alpha);
- obj.invalidate();
- }
- };
-
- private static final Property<WorkspacePageIndicator, Float> NUM_PAGES
- = new Property<WorkspacePageIndicator, Float>(Float.class, "num_pages") {
- @Override
- public Float get(WorkspacePageIndicator obj) {
- return obj.mNumPagesFloat;
- }
-
- @Override
- public void set(WorkspacePageIndicator obj, Float numPages) {
- obj.mNumPagesFloat = numPages;
- obj.invalidate();
- }
- };
-
- private static final Property<WorkspacePageIndicator, Integer> TOTAL_SCROLL
- = new Property<WorkspacePageIndicator, Integer>(Integer.class, "total_scroll") {
- @Override
- public Integer get(WorkspacePageIndicator obj) {
- return obj.mTotalScroll;
- }
-
- @Override
- public void set(WorkspacePageIndicator obj, Integer totalScroll) {
- obj.mTotalScroll = totalScroll;
- obj.invalidate();
- }
- };
-
- private Runnable mHideLineRunnable = () -> animateLineToAlpha(0);
-
- public WorkspacePageIndicator(Context context) {
- this(context, null);
- }
-
- public WorkspacePageIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public WorkspacePageIndicator(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- Resources res = context.getResources();
- mLinePaint = new Paint();
- mLinePaint.setAlpha(0);
-
- mLauncher = Launcher.getLauncher(context);
- mLineHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_line_height);
-
- boolean darkText = Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText);
- mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
- mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mTotalScroll == 0 || mNumPagesFloat == 0) {
- return;
- }
-
- // Compute and draw line rect.
- float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
- int availableWidth = getWidth();
- int lineWidth = (int) (availableWidth / mNumPagesFloat);
- int lineLeft = (int) (progress * (availableWidth - lineWidth));
- int lineRight = lineLeft + lineWidth;
-
- canvas.drawRoundRect(lineLeft, getHeight() / 2 - mLineHeight / 2, lineRight,
- getHeight() / 2 + mLineHeight / 2, mLineHeight, mLineHeight, mLinePaint);
- }
-
- @Override
- public void setScroll(int currentScroll, int totalScroll) {
- if (getAlpha() == 0) {
- return;
- }
- animateLineToAlpha(mActiveAlpha);
-
- mCurrentScroll = currentScroll;
- if (mTotalScroll == 0) {
- mTotalScroll = totalScroll;
- } else if (mTotalScroll != totalScroll) {
- animateToTotalScroll(totalScroll);
- } else {
- invalidate();
- }
-
- if (mShouldAutoHide) {
- hideAfterDelay();
- }
- }
-
- private void hideAfterDelay() {
- mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
- mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
- }
-
- @Override
- public void setActiveMarker(int activePage) { }
-
- @Override
- public void setMarkersCount(int numMarkers) {
- if (Float.compare(numMarkers, mNumPagesFloat) != 0) {
- setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numMarkers),
- NUM_PAGES_ANIMATOR_INDEX);
- } else {
- if (mAnimators[NUM_PAGES_ANIMATOR_INDEX] != null) {
- mAnimators[NUM_PAGES_ANIMATOR_INDEX].cancel();
- mAnimators[NUM_PAGES_ANIMATOR_INDEX] = null;
- }
- }
- }
-
- @Override
- public void setShouldAutoHide(boolean shouldAutoHide) {
- mShouldAutoHide = shouldAutoHide;
- if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
- hideAfterDelay();
- } else if (!shouldAutoHide) {
- mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
- }
- }
-
- private void animateLineToAlpha(int alpha) {
- if (alpha == mToAlpha) {
- // Ignore the new animation if it is going to the same alpha as the current animation.
- return;
- }
- mToAlpha = alpha;
- setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
- LINE_ALPHA_ANIMATOR_INDEX);
- }
-
- private void animateToTotalScroll(int totalScroll) {
- setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
- TOTAL_SCROLL_ANIMATOR_INDEX);
- }
-
- /**
- * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
- * the animation ends.
- *
- * If an animator is already at the index (i.e. it is already playing), it is canceled and
- * replaced with the new animator.
- */
- private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
- if (mAnimators[animatorIndex] != null) {
- mAnimators[animatorIndex].cancel();
- }
- mAnimators[animatorIndex] = animator;
- mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimators[animatorIndex] = null;
- }
- });
- mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
- mAnimators[animatorIndex].start();
- }
-
- /**
- * Pauses all currently running animations.
- */
- @Override
- public void pauseAnimations() {
- for (int i = 0; i < ANIMATOR_COUNT; i++) {
- if (mAnimators[i] != null) {
- mAnimators[i].pause();
- }
- }
- }
-
- /**
- * Force-ends all currently running or paused animations.
- */
- @Override
- public void skipAnimationsToEnd() {
- for (int i = 0; i < ANIMATOR_COUNT; i++) {
- if (mAnimators[i] != null) {
- mAnimators[i].end();
- }
- }
- }
-
- /**
- * We need to override setInsets to prevent InsettableFrameLayout from applying different
- * margins on the page indicator.
- */
- @Override
- public void setInsets(Rect insets) {
- }
-}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index c117be4..856c294 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.Build;
import android.os.UserHandle;
+import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -32,6 +33,7 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
@@ -41,6 +43,8 @@
@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
+ public static final String TAG = "InstallSessionTracker";
+
// Lazily initialized
private SparseArray<PackageUserKey> mActiveSessions = null;
@@ -75,6 +79,11 @@
}
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
if (sessionInfo != null) {
+ FileLog.d(TAG, "onCreated: Install session created for"
+ + " appPackageName=" + sessionInfo.getAppPackageName()
+ + ", sessionId=" + sessionInfo.getSessionId()
+ + ", appIcon=" + sessionInfo.getAppIcon()
+ + ", appLabel=" + sessionInfo.getAppLabel());
callback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo));
}
@@ -102,6 +111,10 @@
activeSessions.remove(sessionId);
if (key != null && key.mPackageName != null) {
+ FileLog.d(TAG, "onFinished: active install session finished for"
+ + " appPackageName=" + key.mPackageName
+ + ", sessionId=" + sessionId
+ + ", success=" + success);
String packageName = key.mPackageName;
PackageInstallInfo info = PackageInstallInfo.fromState(
success ? STATUS_INSTALLED : STATUS_FAILED,
@@ -141,6 +154,11 @@
}
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
if (sessionInfo != null) {
+ Log.d(TAG, "onBadgingChanged: badging info changed for"
+ + " appPackageName=" + sessionInfo.getAppPackageName()
+ + ", sessionId=" + sessionInfo.getSessionId()
+ + ", appIcon=" + sessionInfo.getAppIcon()
+ + ", appLabel=" + sessionInfo.getAppLabel());
helper.tryQueuePromiseAppIcon(sessionInfo);
}
}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 1e577be..37482ac 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -175,8 +175,9 @@
mLauncher.getDeviceProfile(), taskViewDrawAlpha);
if (mFadeOutView != null) {
- // The alpha goes from 1 to 0 when progress is 0 and 0.33 respectively.
- mFadeOutView.setAlpha(1 - Math.min(1f, mapToRange(progress, 0, 0.33f, 0, 1, LINEAR)));
+ // The alpha goes from 1 to 0 when progress is 0 and 0.15 respectively.
+ // This value minimizes view display time while still allowing the view to fade out.
+ mFadeOutView.setAlpha(1 - Math.min(1f, mapToRange(progress, 0, 0.15f, 0, 1, LINEAR)));
}
}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index fa17b7b..63648dd 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -20,6 +20,9 @@
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
+import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.ALL_APPS_SCROLLER;
+import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.WIDGET_SCROLLER;
+
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -40,11 +43,15 @@
import android.view.WindowInsets;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.FastScrollRecyclerView;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.LetterListTextView;
import com.android.launcher3.graphics.FastScrollThumbDrawable;
import com.android.launcher3.util.Themes;
@@ -55,6 +62,19 @@
* The track and scrollbar that shows when you scroll the list.
*/
public class RecyclerViewFastScroller extends View {
+
+ /** FastScrollerLocation describes what RecyclerView the fast scroller is dedicated to. */
+ public enum FastScrollerLocation {
+ UNKNOWN_SCROLLER(0),
+ ALL_APPS_SCROLLER(1),
+ WIDGET_SCROLLER(2);
+
+ public final int location;
+
+ FastScrollerLocation(int location) {
+ this.location = location;
+ }
+ }
private static final String TAG = "RecyclerViewFastScroller";
private static final boolean DEBUG = false;
private static final int FASTSCROLL_THRESHOLD_MILLIS = 40;
@@ -106,6 +126,8 @@
private final Point mThumbDrawOffset = new Point();
private final Paint mTrackPaint;
+ private final int mThumbColor;
+ private final int mThumbLetterScrollerColor;
private float mLastTouchY;
private boolean mIsDragging;
@@ -139,6 +161,7 @@
private int mDownX;
private int mDownY;
private int mLastY;
+ private FastScrollerLocation mFastScrollerLocation;
public RecyclerViewFastScroller(Context context) {
this(context, null);
@@ -151,13 +174,16 @@
public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ mFastScrollerLocation = FastScrollerLocation.UNKNOWN_SCROLLER;
mTrackPaint = new Paint();
mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
+ mThumbColor = Themes.getColorAccent(context);
+ mThumbLetterScrollerColor = Themes.getAttrColor(context, R.attr.materialColorSurfaceBright);
mThumbPaint = new Paint();
mThumbPaint.setAntiAlias(true);
- mThumbPaint.setColor(Themes.getColorAccent(context));
+ mThumbPaint.setColor(mThumbColor);
mThumbPaint.setStyle(Paint.Style.FILL);
Resources res = getResources();
@@ -334,6 +360,18 @@
animatePopupVisibility(!TextUtils.isEmpty(sectionName));
mLastTouchY = boundedY;
setThumbOffsetY((int) mLastTouchY);
+ updateFastScrollerLetterList(y);
+ }
+
+ private void updateFastScrollerLetterList(int y) {
+ if (!shouldUseLetterFastScroller()) {
+ return;
+ }
+ ConstraintLayout mLetterList = mRv.getLetterList();
+ for (int i = 0; i < mLetterList.getChildCount(); i++) {
+ LetterListTextView currentLetter = (LetterListTextView) mLetterList.getChildAt(i);
+ currentLetter.animateBasedOnYPosition(y + mTouchOffsetY);
+ }
}
/** End any active fast scrolling touch handling, if applicable. */
@@ -359,15 +397,35 @@
mThumbDrawOffset.set(getWidth() / 2, mRv.getScrollBarTop());
// Draw the track
float halfW = mWidth / 2;
- canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
- mWidth, mWidth, mTrackPaint);
-
- canvas.translate(0, mThumbOffsetY);
+ boolean useLetterFastScroller = shouldUseLetterFastScroller();
+ if (useLetterFastScroller) {
+ float translateX;
+ if (mIsDragging) {
+ // halfW * 3 is half circle.
+ translateX = halfW * 3;
+ } else {
+ translateX = halfW * 5;
+ }
+ canvas.translate(translateX, mThumbOffsetY);
+ } else {
+ canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
+ mWidth, mWidth, mTrackPaint);
+ canvas.translate(0, mThumbOffsetY);
+ }
mThumbDrawOffset.y += mThumbOffsetY;
+
+ /* Draw half circle */
halfW += mThumbPadding;
float r = getScrollThumbRadius();
- mThumbBounds.set(-halfW, 0, halfW, mThumbHeight);
- canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
+ if (useLetterFastScroller) {
+ mThumbPaint.setColor(mThumbLetterScrollerColor);
+ mThumbBounds.set(0, 0, 0, mThumbHeight);
+ canvas.drawCircle(-halfW, halfW, r * 2, mThumbPaint);
+ } else {
+ mThumbPaint.setColor(mThumbColor);
+ mThumbBounds.set(-halfW, 0, halfW, mThumbHeight);
+ canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
+ }
mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
// swiping very close to the thumb area (not just within it's bound)
// will also prevent back gesture
@@ -380,6 +438,11 @@
canvas.restoreToCount(saveCount);
}
+ boolean shouldUseLetterFastScroller() {
+ return Flags.letterFastScroller()
+ && getScrollerLocation() == FastScrollerLocation.ALL_APPS_SCROLLER;
+ }
+
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mSystemGestureInsets = insets.getSystemGestureInsets();
@@ -421,19 +484,25 @@
return isNearThumb(x, y);
}
- /**
- * Returns whether the specified x position is near the scroll bar.
- */
- public boolean isNearScrollBar(int x) {
- return x >= (getWidth() - mMaxWidth) / 2 - mScrollbarLeftOffsetTouchDelegate
- && x <= (getWidth() + mMaxWidth) / 2;
+ public FastScrollerLocation getScrollerLocation() {
+ return mFastScrollerLocation;
+ }
+
+ public void setFastScrollerLocation(@NonNull FastScrollerLocation location) {
+ mFastScrollerLocation = location;
}
private void animatePopupVisibility(boolean visible) {
if (mPopupVisible != visible) {
mPopupVisible = visible;
- mPopupView.animate().cancel();
- mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
+ if (shouldUseLetterFastScroller()) {
+ mRv.getLetterList().animate().alpha(visible ? 1f : 0f)
+ .setDuration(visible ? 200 : 150).start();
+ } else {
+ mPopupView.animate().cancel();
+ mPopupView.animate().alpha(visible ? 1f : 0f)
+ .setDuration(visible ? 200 : 150).start();
+ }
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 2af8e6f..c8ad564 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.WIDGET_SCROLLER;
import android.animation.Animator;
import android.content.Context;
@@ -119,7 +120,7 @@
WidgetsRecyclerView searchRecyclerView =
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView;
if (mIsInSearchMode && searchRecyclerView != null) {
- searchRecyclerView.bindFastScrollbar(mFastScroller);
+ searchRecyclerView.bindFastScrollbar(mFastScroller, WIDGET_SCROLLER);
}
}
@@ -276,7 +277,7 @@
}
private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
- recyclerView.bindFastScrollbar(mFastScroller);
+ recyclerView.bindFastScrollbar(mFastScroller, WIDGET_SCROLLER);
if (mCurrentWidgetsRecyclerView != recyclerView) {
// Only reset the scroll position & expanded apps if the currently shown recycler view
// has been updated.
@@ -290,10 +291,10 @@
protected void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
// The first item is always an empty space entry. Look for any more items.
boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
- adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) {
mNoWidgetsView.setText(R.string.no_search_results);
+ adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
} else if (adapterHolder.mAdapterType == AdapterHolder.WORK
&& mUserCache.getUserProfiles().stream()
.filter(userHandle -> mUserCache.getUserInfo(userHandle).isWork())
@@ -556,6 +557,8 @@
mNoWidgetsView.setVisibility(GONE);
} else {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
+ mAdapters.get(getCurrentAdapterHolderType()).mWidgetsRecyclerView.setVisibility(
+ VISIBLE);
// Visibility of recommended widgets, recycler views and headers are handled in methods
// below.
post(this::onRecommendedWidgetsBound);
@@ -1057,7 +1060,7 @@
mWidgetsRecyclerView.setClipToOutline(true);
mWidgetsRecyclerView.setClipChildren(false);
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
- mWidgetsRecyclerView.bindFastScrollbar(mFastScroller);
+ mWidgetsRecyclerView.bindFastScrollbar(mFastScroller, WIDGET_SCROLLER);
mWidgetsRecyclerView.setItemAnimator(isTwoPane() ? null : mWidgetsListItemAnimator);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
if (!isTwoPane()) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index c2cd903..d329674 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -376,17 +376,14 @@
mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
final boolean isChangingHeaders = mSelectedHeader == null
|| !mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey);
- // If the initial focus view is still focused, it is likely a programmatic header
- // click.
- if (mSelectedHeader != null
- && !getAccessibilityInitialFocusView().isAccessibilityFocused()) {
- post(() -> {
- mRightPaneScrollView.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
- mRightPaneScrollView.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- });
- }
if (isChangingHeaders) {
+ // If the initial focus view is still focused or widget picker is still opening, it
+ // is likely a programmatic header click.
+ if (mSelectedHeader != null && !mOpenCloseAnimation.getAnimationPlayer().isRunning()
+ && !getAccessibilityInitialFocusView().isAccessibilityFocused()) {
+ mRightPaneScrollView.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
+ focusOnFirstWidgetCell(mWidgetRecommendationsView);
+ }
// If switching from another header, unselect any WidgetCells. This is necessary
// because we do not clear/recycle the WidgetCells in the recommendations container
// when the header is clicked, only when onRecommendationsBound is called. That
@@ -464,6 +461,13 @@
if (!isWidgetAvailable) {
mRightPane.removeAllViews();
mRightPane.addView(mNoWidgetsView);
+ // with no widgets message, no header is selected on left
+ if (mSuggestedWidgetsPackageUserKey != null
+ && mSuggestedWidgetsPackageUserKey.equals(mSelectedHeader)
+ && mSuggestedWidgetsHeader != null) {
+ mSuggestedWidgetsHeader.setExpanded(false);
+ }
+ mSelectedHeader = null;
}
super.updateRecyclerViewVisibility(adapterHolder);
}
@@ -505,9 +509,10 @@
public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
final boolean isSameHeader = mSelectedHeader != null
&& mSelectedHeader.equals(selectedHeader);
- // If the initial focus view is still focused, it is likely a programmatic header
- // click.
+ // If the initial focus view is still focused or widget picker is still opening, it
+ // is likely a programmatic header click.
final boolean isUserClick = mSelectedHeader != null
+ && !mOpenCloseAnimation.getAnimationPlayer().isRunning()
&& !getAccessibilityInitialFocusView().isAccessibilityFocused();
mSelectedHeader = selectedHeader;
final boolean showDefaultWidgets = mWidgetOptionsMenuState != null
diff --git a/tests/Android.bp b/tests/Android.bp
index c99f656..1fa6e05 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -160,7 +160,7 @@
}
filegroup {
- name: "launcher-testing-helpers",
+ name: "launcher-testing-helpers-robo",
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -174,11 +174,20 @@
// Test classes
"src/**/*Test.java",
"src/**/*Test.kt",
+ "src/**/RoboApiWrapper.kt",
"multivalentTests/src/**/*Test.java",
"multivalentTests/src/**/*Test.kt",
],
}
+filegroup {
+ name: "launcher-testing-helpers",
+ srcs: [
+ ":launcher-testing-helpers-robo",
+ "src/**/RoboApiWrapper.kt",
+ ],
+}
+
android_robolectric_test {
enabled: true,
name: "Launcher3RoboTests",
@@ -186,7 +195,7 @@
":launcher3-robo-src",
// Test util classes
- ":launcher-testing-helpers",
+ ":launcher-testing-helpers-robo",
":launcher-testing-shared",
],
exclude_srcs: [
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 a20b0f1..ea58136 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -171,7 +171,6 @@
public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String OVERVIEW_SELECT_TOOLTIP_MISALIGNED = "b/332485341";
- public static final String OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH = "b/336660988";
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/celllayout/ReorderAlgorithmUnitTest.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index 30953cc..a62258c 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -17,6 +17,9 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -37,6 +40,8 @@
import com.android.launcher3.celllayout.testgenerator.RandomBoardGenerator;
import com.android.launcher3.celllayout.testgenerator.RandomMultiBoardGenerator;
import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import org.junit.Rule;
@@ -68,12 +73,16 @@
private Context mApplicationContext;
@Rule
+ public TestStabilityRule mTestStabilityRule = new TestStabilityRule();
+
+ @Rule
public UnitTestCellLayoutBuilderRule mCellLayoutBuilder = new UnitTestCellLayoutBuilderRule();
/**
* This test reads existing test cases and makes sure the CellLayout produces the same
* output for each of them for a given input.
*/
+ @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
@Test
public void testAllCases() throws IOException {
List<ReorderAlgorithmUnitTestCase> testCases = getTestCases(
@@ -116,6 +125,7 @@
/**
* Same as above but testing the Multipage CellLayout.
*/
+ @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
@Test
public void generateValidTests_Multi() {
Random generator = new Random(SEED);
diff --git a/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
diff --git a/tests/src/com/android/launcher3/folder/FolderPagedViewTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderPagedViewTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/folder/FolderPagedViewTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderPagedViewTest.kt
diff --git a/tests/src/com/android/launcher3/folder/FolderTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/folder/FolderTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/folder/FolderTest.kt
diff --git a/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
rename to tests/multivalentTests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
index 7242e9c..b9b7d6a 100644
--- a/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
@@ -24,15 +24,15 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.PathInterpolator;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.launcher3.CellLayout;
+import com.android.launcher3.util.LauncherMultivalentJUnit;
import org.junit.Before;
import org.junit.Test;
@@ -41,8 +41,8 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@UiThreadTest
+@RunWith(LauncherMultivalentJUnit.class)
public class PreviewBackgroundTest {
private static final float REST_SCALE = 1f;
diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
similarity index 98%
rename from tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index 7c9f99a..d236551 100644
--- a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -29,6 +29,7 @@
import com.android.launcher3.icons.BaseIconFactory
import com.android.launcher3.icons.FastBitmapDrawable
import com.android.launcher3.icons.UserBadgeDrawable
+import com.android.launcher3.model.ModelTestRule
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED
@@ -44,6 +45,7 @@
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.junit.runner.RunWith
@@ -52,6 +54,8 @@
@RunWith(AndroidJUnit4::class)
class PreviewItemManagerTest {
+ @get:Rule val modelTestRule = ModelTestRule()
+
private lateinit var previewItemManager: PreviewItemManager
private lateinit var context: Context
private lateinit var folderItems: ArrayList<ItemInfo>
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
similarity index 98%
rename from tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 370af0c..43dc36b 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -27,6 +27,7 @@
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.junit.runner.RunWith
import org.mockito.Mockito.times
@@ -43,6 +44,8 @@
@RunWith(AndroidJUnit4::class)
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
+ @get:Rule val modelTestRule = ModelTestRule()
+
private lateinit var mDataModelCallbacks: MyCallbacks
private val mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder = mock()
diff --git a/tests/src/com/android/launcher3/model/AsyncBindingTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt
similarity index 99%
rename from tests/src/com/android/launcher3/model/AsyncBindingTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt
index af367a8..dce75b9 100644
--- a/tests/src/com/android/launcher3/model/AsyncBindingTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt
@@ -64,6 +64,8 @@
@get:Rule val setFlagsRule = SetFlagsRule()
+ @get:Rule val modelTestRule = ModelTestRule()
+
@Spy private var callbacks = MyCallbacks()
@Mock private lateinit var itemInflater: ItemInflater<*>
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
similarity index 84%
rename from tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
rename to tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 328558d..535080a 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -1,3 +1,18 @@
+/*
+ * 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 static android.os.Process.myUserHandle;
@@ -11,10 +26,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.content.pm.PackageInstaller;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -49,6 +67,9 @@
@Rule(order = 0)
public TestRule testStabilityRule = new TestStabilityRule();
+ @Rule(order = 1)
+ public ModelTestRule mModelTestRule = new ModelTestRule();
+
private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
@@ -128,10 +149,13 @@
@Test
public void testSessionUpdate_updates_pending_apps() {
// Run on model executor so that no other task runs in the middle.
+ PackageInstaller.SessionInfo sessionInfo = ApplicationProvider.getApplicationContext()
+ .getPackageManager().getPackageInstaller().getSessionInfo(mSession1);
+ assertNotNull(sessionInfo);
runOnExecutorSync(MODEL_EXECUTOR, () -> {
LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache(
new PackageUserKey(PENDING_APP_1, myUserHandle()),
- mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1));
+ sessionInfo);
// Clear all icons from apps list so that its easy to check what was updated
allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
diff --git a/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
similarity index 93%
rename from tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
rename to tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 10785f7..e14e145 100644
--- a/tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -22,11 +22,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -39,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,8 +50,10 @@
@RunWith(AndroidJUnit4.class)
public class DefaultLayoutProviderTest {
+ @Rule public ModelTestRule rule = new ModelTestRule();
+
private LauncherModelHelper mModelHelper;
- private Context mTargetContext;
+ private LauncherModelHelper.SandboxModelContext mTargetContext;
@Before
public void setUp() {
@@ -114,8 +117,10 @@
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(pendingAppPkg);
params.setAppIcon(BitmapInfo.LOW_RES_ICON);
+ params.installerPackageName = ApplicationProvider.getApplicationContext().getPackageName();
- PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
+ PackageInstaller installer = ApplicationProvider.getApplicationContext().getPackageManager()
+ .getPackageInstaller();
installer.createSession(params);
writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
diff --git a/tests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
similarity index 98%
rename from tests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
index aadf72e..d2d9512 100644
--- a/tests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
@@ -21,6 +21,7 @@
import android.content.Intent
import android.content.pm.PackageInstaller.SessionInfo
import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
@@ -33,14 +34,20 @@
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
import junit.framework.Assert.assertEquals
+import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+@RunWith(AndroidJUnit4::class)
class FirstScreenBroadcastHelperTest {
+
+ @get:Rule val modelTestRule = ModelTestRule()
+
private val context = spy(InstrumentationRegistry.getInstrumentation().targetContext)
private val mockPmHelper = mock<PackageManagerHelper>()
private val expectedAppPackage = "appPackageExpected"
diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
similarity index 96%
rename from tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index c4a4c9b..d002493 100644
--- a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -23,12 +23,14 @@
import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.LauncherModelHelper.*
+import com.android.launcher3.util.RoboApiWrapper
import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import java.util.concurrent.CountDownLatch
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,6 +38,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class FolderIconLoadTest {
+
+ @get:Rule(order = 0) val modelTestRule = ModelTestRule()
+
private lateinit var modelHelper: LauncherModelHelper
private val uniqueActivities =
@@ -145,6 +150,7 @@
while (cache.isIconUpdateInProgress) {
val wait = CountDownLatch(1)
Executors.MODEL_EXECUTOR.handler.postDelayed({ wait.countDown() }, 10)
+ RoboApiWrapper.waitForLooperSync(Executors.MODEL_EXECUTOR.handler.looper)
wait.await()
}
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { cache.clearMemoryCache() }
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/model/LoaderCursorTest.java
rename to tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index b4945d7..ac911b3 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -67,6 +67,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +78,8 @@
@RunWith(AndroidJUnit4.class)
public class LoaderCursorTest {
+ @Rule public ModelTestRule rule = new ModelTestRule();
+
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
private PackageManagerHelper mPmHelper;
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt b/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt
new file mode 100644
index 0000000..ad2c2a4
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt
@@ -0,0 +1,27 @@
+/*
+ * 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 com.android.launcher3.util.RoboApiWrapper
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+class ModelTestRule : TestWatcher() {
+ override fun starting(description: Description?) {
+ RoboApiWrapper.initialize()
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
similarity index 87%
rename from tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
rename to tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index 4ba61ac..a0d9da9 100644
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -1,3 +1,18 @@
+/*
+ * 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 static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -22,6 +37,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +48,8 @@
@RunWith(AndroidJUnit4.class)
public class PackageInstallStateChangedTaskTest {
+ @Rule public ModelTestRule mModelTestRule = new ModelTestRule();
+
private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
similarity index 81%
rename from tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index 6cf3b19..1d9c161 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -25,10 +25,9 @@
import android.content.pm.ShortcutInfo
import android.os.Process
import android.os.UserHandle
-import android.platform.test.annotations.EnableFlags
import android.util.LongSparseArray
-import com.android.dx.mockito.inline.extended.ExtendedMockito
-import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
@@ -36,7 +35,6 @@
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
-import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.EMPTY_PERSON_ARRAY
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError.Companion.MISSING_INFO
@@ -46,7 +44,6 @@
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo
-import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
@@ -57,11 +54,12 @@
import com.android.launcher3.util.UserIconInfo
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
import com.android.launcher3.widget.WidgetInflater
-import com.android.launcher3.widget.WidgetSections
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.RETURNS_DEEP_STUBS
@@ -74,10 +72,12 @@
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-import org.mockito.quality.Strictness
+@RunWith(AndroidJUnit4::class)
class WorkspaceItemProcessorTest {
+ @get:Rule val modelTestRule = ModelTestRule()
+
@Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
@Mock private lateinit var mockBgDataModel: BgDataModel
@@ -122,6 +122,7 @@
mock<Context>().apply {
whenever(packageManager).thenReturn(mock())
whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
+ whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
}
mockAppState =
mock<LauncherAppState>().apply {
@@ -666,142 +667,6 @@
}
@Test
- fun `When Pending App Widget has not started restore then update db and add item`() {
-
- val mockitoSession =
- ExtendedMockito.mockitoSession()
- .strictness(Strictness.LENIENT)
- .mockStatic(WidgetSections::class.java)
- .startMocking()
- try {
- // Given
- val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
- val expectedComponentName =
- ComponentName.unflattenFromString(expectedProvider)!!.flattenToString()
- val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED
- val expectedAppWidgetId = 0
- mockCursor.apply {
- itemType = ITEM_TYPE_APPWIDGET
- user = mUserHandle
- restoreFlag = FLAG_UI_NOT_READY
- container = CONTAINER_DESKTOP
- whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
- whenever(appWidgetProvider).thenReturn(expectedProvider)
- whenever(appWidgetId).thenReturn(expectedAppWidgetId)
- whenever(spanX).thenReturn(2)
- whenever(spanY).thenReturn(1)
- whenever(options).thenReturn(0)
- whenever(appWidgetSource).thenReturn(20)
- whenever(applyCommonProperties(any())).thenCallRealMethod()
- whenever(
- updater()
- .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName)
- .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
- .put(Favorites.RESTORED, expectedRestoreStatus)
- .commit()
- )
- .thenReturn(1)
- }
- val inflationResult =
- WidgetInflater.InflationResult(
- type = WidgetInflater.TYPE_PENDING,
- widgetInfo = null
- )
- mockWidgetInflater =
- mock<WidgetInflater>().apply {
- whenever(inflateAppWidget(any())).thenReturn(inflationResult)
- }
- val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle)
- mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo()
-
- // When
- itemProcessorUnderTest =
- createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
- itemProcessorUnderTest.processItem()
-
- // Then
- val expectedWidgetInfo =
- LauncherAppWidgetInfo().apply {
- appWidgetId = expectedAppWidgetId
- providerName = ComponentName.unflattenFromString(expectedProvider)
- restoreStatus = expectedRestoreStatus
- }
- verify(
- mockCursor
- .updater()
- .put(Favorites.APPWIDGET_PROVIDER, expectedProvider)
- .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
- .put(Favorites.RESTORED, expectedRestoreStatus)
- )
- .commit()
- val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
- verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
- val actualWidgetInfo = widgetInfoCaptor.value
- with(actualWidgetInfo) {
- assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
- assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus)
- assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent)
- assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId)
- }
- } finally {
- mockitoSession.finishMocking()
- }
- }
-
- @Test
- @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
- fun `When Archived Pending App Widget then checkAndAddItem`() {
- val mockitoSession =
- ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking()
- try {
- // Given
- val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
- val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
- val expectedPackage = expectedComponentName!!.packageName
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(isAppArchived(expectedPackage)).thenReturn(true)
- }
- mockCursor =
- mock<LoaderCursor>().apply {
- itemType = ITEM_TYPE_APPWIDGET
- id = 1
- user = UserHandle(1)
- restoreFlag = FLAG_UI_NOT_READY
- container = CONTAINER_DESKTOP
- whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
- whenever(appWidgetProvider).thenReturn(expectedProvider)
- whenever(appWidgetId).thenReturn(0)
- whenever(spanX).thenReturn(2)
- whenever(spanY).thenReturn(1)
- whenever(options).thenReturn(0)
- whenever(appWidgetSource).thenReturn(20)
- whenever(applyCommonProperties(any())).thenCallRealMethod()
- }
- mInstallingPkgs = hashMapOf()
- val inflationResult =
- WidgetInflater.InflationResult(
- type = WidgetInflater.TYPE_PENDING,
- widgetInfo = null
- )
- mockWidgetInflater =
- mock<WidgetInflater>().apply {
- whenever(inflateAppWidget(any())).thenReturn(inflationResult)
- }
- itemProcessorUnderTest =
- createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
-
- // When
- itemProcessorUnderTest.processItem()
-
- // Then
- verify(mockCursor).checkAndAddItem(any(), any())
- } finally {
- mockitoSession.finishMocking()
- }
- }
-
- @Test
fun `When widget inflation result is TYPE_DELETE then mark deleted`() {
// Given
val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
similarity index 98%
rename from tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
index b3d02be..ae8e966 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
@@ -21,6 +21,7 @@
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.junit.runner.RunWith
@@ -29,6 +30,8 @@
@RunWith(AndroidJUnit4::class)
class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() {
+ @get:Rule val modelTestRule = ModelTestRule()
+
private val mItemSpaceFinder = WorkspaceItemSpaceFinder()
@Before
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt b/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
similarity index 94%
rename from tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
index b531adb..d860710 100644
--- a/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
@@ -26,6 +26,7 @@
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
+import com.android.launcher3.model.ModelTestRule
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.PackageUserKey
@@ -35,6 +36,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
@@ -43,7 +45,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class InstallSessionTrackerTest {
- @get:Rule val setFlagsRule = SetFlagsRule()
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
+ @get:Rule(order = 1) val modelTestRule = ModelTestRule()
private val mockInstallSessionHelper: InstallSessionHelper = mock()
private val mockCallback: InstallSessionTracker.Callback = mock()
@@ -200,13 +204,9 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
fun `register triggers registerPackageInstallerSessionCallback for versions from Q`() {
// Given
- whenever(
- launcherApps.registerPackageInstallerSessionCallback(
- MODEL_EXECUTOR,
- installSessionTracker
- )
- )
- .then { /* no-op */ }
+ doNothing()
+ .whenever(launcherApps)
+ .registerPackageInstallerSessionCallback(MODEL_EXECUTOR, installSessionTracker)
// When
installSessionTracker.register()
// Then
@@ -218,8 +218,9 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
fun `unregister triggers unregisterPackageInstallerSessionCallback for versions from Q`() {
// Given
- whenever(launcherApps.unregisterPackageInstallerSessionCallback(installSessionTracker))
- .then { /* no-op */ }
+ doNothing()
+ .whenever(launcherApps)
+ .unregisterPackageInstallerSessionCallback(installSessionTracker)
// When
installSessionTracker.unregister()
// Then
diff --git a/tests/src/com/android/launcher3/pm/UserCacheTest.kt b/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt
similarity index 97%
rename from tests/src/com/android/launcher3/pm/UserCacheTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt
index b21219e..482dced 100644
--- a/tests/src/com/android/launcher3/pm/UserCacheTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt
@@ -20,6 +20,7 @@
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.model.ModelTestRule
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.TestUtil
@@ -27,11 +28,15 @@
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.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class UserCacheTest {
+
+ @get:Rule val modelTestRule = ModelTestRule()
+
private val launcherModelHelper = LauncherModelHelper()
private val sandboxContext = launcherModelHelper.sandboxContext
private lateinit var userCache: UserCache
diff --git a/tests/multivalentTests/src/com/android/launcher3/settings/SettingsActivityTest.java b/tests/multivalentTests/src/com/android/launcher3/settings/SettingsActivityTest.java
deleted file mode 100644
index 10e0be8..0000000
--- a/tests/multivalentTests/src/com/android/launcher3/settings/SettingsActivityTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.settings;
-
-import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem;
-import static androidx.test.espresso.intent.Intents.intended;
-import static androidx.test.espresso.intent.matcher.BundleMatchers.hasEntry;
-import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
-import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
-import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.launcher3.settings.SettingsActivity.DEVELOPER_OPTIONS_KEY;
-import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARGS;
-import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ROOT_KEY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.equalTo;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.espresso.intent.Intents;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.launcher3.R;
-import com.android.systemui.shared.plugins.PluginPrefs;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class SettingsActivityTest {
-
- private Context mApplicationContext;
-
- @Before
- public void setUp() {
- mApplicationContext = ApplicationProvider.getApplicationContext();
- Intents.init();
- }
-
- @After
- public void tearDown() {
- Intents.release();
- }
-
- @Test
- @Ignore // b/199309785
- public void testSettings_aboutTap_launchesActivity() {
- ActivityScenario.launch(SettingsActivity.class);
- onView(withId(R.id.recycler_view)).perform(
- actionOnItem(hasDescendant(withText("About")), click()));
-
- intended(allOf(
- hasComponent(SettingsActivity.class.getName()),
- hasExtra(
- equalTo(EXTRA_FRAGMENT_ARGS),
- hasEntry(ARG_PREFERENCE_ROOT, "about_screen"))));
- }
-
- @Test
- @Ignore // b/199309785
- public void testSettings_developerOptionsTap_launchesActivityWithFragment() {
- PluginPrefs.setHasPlugins(mApplicationContext);
- ActivityScenario.launch(SettingsActivity.class);
- onView(withId(R.id.recycler_view)).perform(
- actionOnItem(hasDescendant(withText("Developer Options")), click()));
-
- intended(allOf(
- hasComponent(SettingsActivity.class.getName()),
- hasExtra(EXTRA_FRAGMENT_ROOT_KEY, DEVELOPER_OPTIONS_KEY)));
- }
-
- @Test
- @Ignore // b/199309785
- public void testSettings_aboutScreenIntent() {
- Bundle fragmentArgs = new Bundle();
- fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
-
- Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
- .putExtra(EXTRA_FRAGMENT_ARGS, fragmentArgs);
- ActivityScenario.launch(intent);
-
- onView(withText("About")).check(matches(isDisplayed()));
- onView(withText("Version")).check(matches(isDisplayed()));
- onView(withContentDescription("Navigate up")).check(matches(isDisplayed()));
- }
-
- @Test
- @Ignore // b/199309785
- public void testSettings_developerOptionsFragmentIntent() {
- Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
- .putExtra(EXTRA_FRAGMENT_ROOT_KEY, DEVELOPER_OPTIONS_KEY);
- ActivityScenario.launch(intent);
-
- onView(withText("Developer Options")).check(matches(isDisplayed()));
- onView(withId(R.id.filter_box)).check(matches(isDisplayed()));
- onView(withContentDescription("Navigate up")).check(matches(isDisplayed()));
- }
-
- @Test
- @Ignore // b/199309785
- public void testSettings_backButtonFinishesActivity() {
- Bundle fragmentArgs = new Bundle();
- fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
- Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
- .putExtra(EXTRA_FRAGMENT_ARGS, fragmentArgs);
- ActivityScenario<SettingsActivity> scenario = ActivityScenario.launch(intent);
-
- onView(withContentDescription("Navigate up")).perform(click());
- scenario.onActivity(activity -> assertThat(activity.isFinishing()).isTrue());
- }
-}
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
index aa7f388..b933ed2 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/BubbleTextViewTest.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW;
import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT;
import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_SMALL;
+import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
import static com.android.launcher3.Flags.FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS;
import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
@@ -416,7 +417,7 @@
assertThat(mBubbleTextView.getIcon().hasBadge()).isEqualTo(false);
}
- @EnableFlags(FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS)
+ @EnableFlags({FLAG_ENABLE_SUPPORT_FOR_ARCHIVING, FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS})
@Test
public void applyIconAndLabel_setsImageSpan_whenInactiveArchivedApp() {
// Given
@@ -452,7 +453,7 @@
assertThat(actualSpan.getVerticalAlignment()).isEqualTo(ALIGN_CENTER);
}
- @EnableFlags(FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS)
+ @EnableFlags({FLAG_ENABLE_SUPPORT_FOR_ARCHIVING, FLAG_USE_NEW_ICON_FOR_ARCHIVED_APPS})
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
public void applyIconAndLabel_setsBoldDrawable_whenBoldedTextForArchivedApp() {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
index f18c02b..2d53e29 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -45,21 +45,24 @@
import android.util.ArrayMap;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.testing.TestInformationProvider;
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -85,6 +88,23 @@
public static final String TEST_ACTIVITY13 = "com.android.launcher3.tests.Activity14";
public static final String TEST_ACTIVITY14 = "com.android.launcher3.tests.Activity15";
+ public static final List<String> ACTIVITY_LIST = Arrays.asList(
+ TEST_ACTIVITY,
+ TEST_ACTIVITY2,
+ TEST_ACTIVITY3,
+ TEST_ACTIVITY4,
+ TEST_ACTIVITY5,
+ TEST_ACTIVITY6,
+ TEST_ACTIVITY7,
+ TEST_ACTIVITY8,
+ TEST_ACTIVITY9,
+ TEST_ACTIVITY10,
+ TEST_ACTIVITY11,
+ TEST_ACTIVITY12,
+ TEST_ACTIVITY13,
+ TEST_ACTIVITY14
+ );
+
// Authority for providing a test default-workspace-layout data.
private static final String TEST_PROVIDER_AUTHORITY =
LauncherModelHelper.class.getName().toLowerCase();
@@ -128,7 +148,9 @@
icon.eraseColor(Color.RED);
sp.setAppIcon(icon);
sp.setAppLabel(pkg);
- PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller();
+ sp.setInstallerPackageName(ApplicationProvider.getApplicationContext().getPackageName());
+ PackageInstaller pi = ApplicationProvider.getApplicationContext().getPackageManager()
+ .getPackageInstaller();
int sessionId = pi.createSession(sp);
mDestroyTask.add(() -> pi.abandonSession(sessionId));
return sessionId;
@@ -164,11 +186,19 @@
public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
throws Exception {
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(sandboxContext);
- idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE;
- idp.iconBitmapSize = DEFAULT_BITMAP_SIZE;
+ if (idp.numRows == 0 && idp.numColumns == 0) {
+ idp.numRows = idp.numColumns = idp.numDatabaseHotseatIcons = DEFAULT_GRID_SIZE;
+ }
+ if (idp.iconBitmapSize == 0) {
+ idp.iconBitmapSize = DEFAULT_BITMAP_SIZE;
+ }
- UiDevice.getInstance(getInstrumentation()).executeShellCommand(
- "settings put secure launcher3.layout.provider " + TEST_PROVIDER_AUTHORITY);
+ Settings.Secure.putString(sandboxContext.getContentResolver(), "launcher3.layout.provider",
+ TEST_PROVIDER_AUTHORITY);
+
+ // TODO: use a wrapper class to differentiate the behavior
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ builder.build(new OutputStreamWriter(bos));
ContentProvider cp = new TestInformationProvider() {
@Override
@@ -177,8 +207,6 @@
try {
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
AutoCloseOutputStream outputStream = new AutoCloseOutputStream(pipe[1]);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- builder.build(new OutputStreamWriter(bos));
outputStream.write(bos.toByteArray());
outputStream.flush();
outputStream.close();
@@ -189,9 +217,13 @@
}
};
setupProvider(TEST_PROVIDER_AUTHORITY, cp);
+ RoboApiWrapper.INSTANCE.registerInputStream(sandboxContext.getContentResolver(),
+ ModelDbController.getLayoutUri(TEST_PROVIDER_AUTHORITY, sandboxContext),
+ ()-> new ByteArrayInputStream(bos.toByteArray()));
+
mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () ->
- UiDevice.getInstance(getInstrumentation()).executeShellCommand(
- "settings delete secure launcher3.layout.provider")));
+ Settings.Secure.putString(sandboxContext.getContentResolver(),
+ "launcher3.layout.provider", "")));
return this;
}
@@ -203,7 +235,7 @@
MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
- MAIN_EXECUTOR.submit(() -> { }).get();
+ getInstrumentation().waitForIdleSync();
MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
}
diff --git a/tests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt
similarity index 86%
rename from tests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt
index 011d138..d26c4d4 100644
--- a/tests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ViewOnDrawExecutorTest.kt
@@ -32,7 +32,7 @@
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.reset
import org.mockito.kotlin.same
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
@RunWith(AndroidJUnit4::class)
class ViewOnDrawExecutorTest<T> where T : View, T : PageIndicator {
@@ -77,8 +77,8 @@
underTest.attachTo(launcher)
verify(workspace).addOnAttachStateChangeListener(same(underTest))
- verifyZeroInteractions(viewTreeObserver)
- verifyZeroInteractions(rootView)
+ verifyNoMoreInteractions(viewTreeObserver)
+ verifyNoMoreInteractions(rootView)
}
@Test
@@ -100,8 +100,8 @@
underTest.onViewAttachedToWindow(rootView)
- verifyZeroInteractions(viewTreeObserver)
- verifyZeroInteractions(rootView)
+ verifyNoMoreInteractions(viewTreeObserver)
+ verifyNoMoreInteractions(rootView)
}
@Test
@@ -117,10 +117,10 @@
fun run_before_onDraw_noOp() {
underTest.run()
- verifyZeroInteractions(runnable)
- verifyZeroInteractions(viewTreeObserver)
- verifyZeroInteractions(workspace)
- verifyZeroInteractions(consumer)
+ verifyNoMoreInteractions(runnable)
+ verifyNoMoreInteractions(viewTreeObserver)
+ verifyNoMoreInteractions(workspace)
+ verifyNoMoreInteractions(consumer)
}
@Test
@@ -148,10 +148,10 @@
underTest.run()
- verifyZeroInteractions(runnable)
- verifyZeroInteractions(viewTreeObserver)
- verifyZeroInteractions(workspace)
- verifyZeroInteractions(consumer)
+ verifyNoMoreInteractions(runnable)
+ verifyNoMoreInteractions(viewTreeObserver)
+ verifyNoMoreInteractions(workspace)
+ verifyNoMoreInteractions(consumer)
}
@Test
@@ -160,7 +160,7 @@
verify(runnable).run()
verify(consumer).accept(underTest)
- verifyZeroInteractions(workspace)
+ verifyNoMoreInteractions(workspace)
}
@Test
@@ -179,8 +179,8 @@
fun cancel_notRun() {
underTest.cancel()
- verifyZeroInteractions(runnable)
+ verifyNoMoreInteractions(runnable)
verify(consumer).accept(underTest)
- verifyZeroInteractions(workspace)
+ verifyNoMoreInteractions(workspace)
}
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 909aabd..ad2d8c2 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -109,6 +109,9 @@
getPackageManager().
getPackageInfo(launcherPackageName, 0)
.versionName;
+ if (launcherVersion == null) {
+ return LOCAL;
+ }
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index 05a1224..c7c9dbb 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -25,7 +25,6 @@
import android.content.Intent;
import android.platform.test.annotations.PlatinumTest;
-import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.launcher3.Launcher;
@@ -33,7 +32,6 @@
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
-import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Test;
@@ -191,7 +189,6 @@
/**
* Makes sure that when pressing back when AllApps is open we go back to the Home screen.
*/
- @FlakyTest(bugId = 256615483)
@Test
@PortraitLandscape
public void testPressBackFromAllAppsToHome() {
diff --git a/tests/src/com/android/launcher3/celllayout/integrationtest/TestUtils.kt b/tests/src/com/android/launcher3/celllayout/integrationtest/TestUtils.kt
index 4cecb5a..bcb9191 100644
--- a/tests/src/com/android/launcher3/celllayout/integrationtest/TestUtils.kt
+++ b/tests/src/com/android/launcher3/celllayout/integrationtest/TestUtils.kt
@@ -21,6 +21,7 @@
import android.view.View
import android.view.ViewGroup
import com.android.launcher3.CellLayout
+import com.android.launcher3.Utilities
import com.android.launcher3.Workspace
import com.android.launcher3.util.CellAndSpan
import com.android.launcher3.widget.LauncherAppWidgetHostView
@@ -54,7 +55,7 @@
return view as LauncherAppWidgetHostView
}
- fun getCellTopLeftRelativeToCellLayout(
+ fun getCellTopLeftRelativeToWorkspace(
workspace: Workspace<*>,
cellAndSpan: CellAndSpan
): Point {
@@ -67,6 +68,8 @@
cellAndSpan.spanY,
target
)
- return Point(target.left, target.top)
+ val point = floatArrayOf(target.left.toFloat(), target.top.toFloat())
+ Utilities.getDescendantCoordRelativeToAncestor(cellLayout, workspace, point, false)
+ return Point(point[0].toInt(), point[1].toInt())
}
}
diff --git a/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt b/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
index d9af07a..05f626d 100644
--- a/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
@@ -58,7 +58,8 @@
@RunWith(AndroidJUnit4::class)
class PackageUpdatedTaskTest {
- @get:Rule val setFlagsRule = SetFlagsRule()
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+ @get:Rule(order = 1) val modelTestRule = ModelTestRule()
private val mUser = UserHandle(0)
private val mDataModel: BgDataModel = BgDataModel()
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
new file mode 100644
index 0000000..b93c305
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
@@ -0,0 +1,338 @@
+/*
+ * 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.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.LauncherApps
+import android.content.pm.PackageInstaller
+import android.content.pm.ShortcutInfo
+import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.util.LongSparseArray
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.Utilities
+import com.android.launcher3.model.data.IconRequestInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.PackageManagerHelper
+import com.android.launcher3.util.PackageUserKey
+import com.android.launcher3.util.UserIconInfo
+import com.android.launcher3.widget.WidgetInflater
+import com.android.launcher3.widget.WidgetSections
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class WorkspaceItemProcessorExtraTest {
+
+ @Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
+ @Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
+ @Mock private lateinit var mockBgDataModel: BgDataModel
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockAppState: LauncherAppState
+ @Mock private lateinit var mockPmHelper: PackageManagerHelper
+ @Mock private lateinit var mockLauncherApps: LauncherApps
+ @Mock private lateinit var mockCursor: LoaderCursor
+ @Mock private lateinit var mockUserCache: UserCache
+ @Mock private lateinit var mockUserManagerState: UserManagerState
+ @Mock private lateinit var mockWidgetInflater: WidgetInflater
+
+ private var intent: Intent = Intent()
+ private var mUserHandle: UserHandle = UserHandle(0)
+ private var mIconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mutableListOf()
+ private var mComponentName: ComponentName = ComponentName("package", "class")
+ private var mUnlockedUsersArray: LongSparseArray<Boolean> = LongSparseArray()
+ private var mKeyToPinnedShortcutsMap: MutableMap<ShortcutKey, ShortcutInfo> = mutableMapOf()
+ private var mInstallingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = hashMapOf()
+ private var mAllDeepShortcuts: MutableList<ShortcutInfo> = mutableListOf()
+ private var mWidgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> =
+ mutableMapOf()
+ private var mPendingPackages: MutableSet<PackageUserKey> = mutableSetOf()
+
+ private lateinit var itemProcessorUnderTest: WorkspaceItemProcessor
+
+ @Before
+ fun setup() {
+ mUserHandle = UserHandle(0)
+ mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
+ mockWorkspaceInfo = mock<WorkspaceItemInfo>()
+ mockBgDataModel = mock<BgDataModel>()
+ mComponentName = ComponentName("package", "class")
+ mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
+ intent =
+ Intent().apply {
+ component = mComponentName
+ `package` = "pkg"
+ putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
+ }
+ mockContext =
+ mock<Context>().apply {
+ whenever(packageManager).thenReturn(mock())
+ whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
+ whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
+ }
+ mockAppState =
+ mock<LauncherAppState>().apply {
+ whenever(context).thenReturn(mockContext)
+ whenever(iconCache).thenReturn(mock())
+ whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
+ }
+ mockPmHelper =
+ mock<PackageManagerHelper>().apply {
+ whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
+ .thenReturn(intent)
+ }
+ mockLauncherApps =
+ mock<LauncherApps>().apply {
+ whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+ whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
+ }
+ mockCursor =
+ Mockito.mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
+ user = mUserHandle
+ itemType = ITEM_TYPE_APPLICATION
+ id = 1
+ restoreFlag = 1
+ serialNumber = 101
+ whenever(parseIntent()).thenReturn(intent)
+ whenever(markRestored()).doAnswer { restoreFlag = 0 }
+ whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1)
+ whenever(getAppShortcutInfo(any(), any(), any(), any()))
+ .thenReturn(mockWorkspaceInfo)
+ whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
+ }
+ mockUserCache =
+ mock<UserCache>().apply {
+ val userIconInfo =
+ mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
+ whenever(getUserInfo(any())).thenReturn(userIconInfo)
+ }
+
+ mockUserManagerState = mock<UserManagerState>()
+ mockWidgetInflater = mock<WidgetInflater>()
+ mKeyToPinnedShortcutsMap = mutableMapOf()
+ mInstallingPkgs = hashMapOf()
+ mAllDeepShortcuts = mutableListOf()
+ mWidgetProvidersMap = mutableMapOf()
+ mIconRequestInfos = mutableListOf()
+ mPendingPackages = mutableSetOf()
+ }
+
+ @Test
+ fun `When Pending App Widget has not started restore then update db and add item`() {
+
+ val mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(WidgetSections::class.java)
+ .startMocking()
+ try {
+ // Given
+ val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+ val expectedComponentName =
+ ComponentName.unflattenFromString(expectedProvider)!!.flattenToString()
+ val expectedRestoreStatus = FLAG_UI_NOT_READY or FLAG_RESTORE_STARTED
+ val expectedAppWidgetId = 0
+ mockCursor.apply {
+ itemType = ITEM_TYPE_APPWIDGET
+ user = mUserHandle
+ restoreFlag = FLAG_UI_NOT_READY
+ container = CONTAINER_DESKTOP
+ whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+ whenever(appWidgetProvider).thenReturn(expectedProvider)
+ whenever(appWidgetId).thenReturn(expectedAppWidgetId)
+ whenever(spanX).thenReturn(2)
+ whenever(spanY).thenReturn(1)
+ whenever(options).thenReturn(0)
+ whenever(appWidgetSource).thenReturn(20)
+ whenever(applyCommonProperties(any())).thenCallRealMethod()
+ whenever(
+ updater()
+ .put(Favorites.APPWIDGET_PROVIDER, expectedComponentName)
+ .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
+ .put(Favorites.RESTORED, expectedRestoreStatus)
+ .commit()
+ )
+ .thenReturn(1)
+ }
+ val inflationResult =
+ WidgetInflater.InflationResult(
+ type = WidgetInflater.TYPE_PENDING,
+ widgetInfo = null
+ )
+ mockWidgetInflater =
+ mock<WidgetInflater>().apply {
+ whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+ }
+ val packageUserKey = PackageUserKey("com.google.android.testApp", mUserHandle)
+ mInstallingPkgs[packageUserKey] = PackageInstaller.SessionInfo()
+
+ // When
+ itemProcessorUnderTest =
+ createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+ itemProcessorUnderTest.processItem()
+
+ // Then
+ val expectedWidgetInfo =
+ LauncherAppWidgetInfo().apply {
+ appWidgetId = expectedAppWidgetId
+ providerName = ComponentName.unflattenFromString(expectedProvider)
+ restoreStatus = expectedRestoreStatus
+ }
+ verify(
+ mockCursor
+ .updater()
+ .put(Favorites.APPWIDGET_PROVIDER, expectedProvider)
+ .put(Favorites.APPWIDGET_ID, expectedAppWidgetId)
+ .put(Favorites.RESTORED, expectedRestoreStatus)
+ )
+ .commit()
+ val widgetInfoCaptor = ArgumentCaptor.forClass(LauncherAppWidgetInfo::class.java)
+ verify(mockCursor).checkAndAddItem(widgetInfoCaptor.capture(), eq(mockBgDataModel))
+ val actualWidgetInfo = widgetInfoCaptor.value
+ with(actualWidgetInfo) {
+ assertThat(providerName).isEqualTo(expectedWidgetInfo.providerName)
+ assertThat(restoreStatus).isEqualTo(expectedWidgetInfo.restoreStatus)
+ assertThat(targetComponent).isEqualTo(expectedWidgetInfo.targetComponent)
+ assertThat(appWidgetId).isEqualTo(expectedWidgetInfo.appWidgetId)
+ }
+ } finally {
+ mockitoSession.finishMocking()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+ fun `When Archived Pending App Widget then checkAndAddItem`() {
+ val mockitoSession =
+ ExtendedMockito.mockitoSession().mockStatic(Utilities::class.java).startMocking()
+ try {
+ // Given
+ val expectedProvider = "com.google.android.testApp/com.android.testApp.testAppProvider"
+ val expectedComponentName = ComponentName.unflattenFromString(expectedProvider)
+ val expectedPackage = expectedComponentName!!.packageName
+ mockPmHelper =
+ mock<PackageManagerHelper>().apply {
+ whenever(isAppArchived(expectedPackage)).thenReturn(true)
+ }
+ mockCursor =
+ mock<LoaderCursor>().apply {
+ itemType = ITEM_TYPE_APPWIDGET
+ id = 1
+ user = UserHandle(1)
+ restoreFlag = FLAG_UI_NOT_READY
+ container = CONTAINER_DESKTOP
+ whenever(isOnWorkspaceOrHotseat).thenCallRealMethod()
+ whenever(appWidgetProvider).thenReturn(expectedProvider)
+ whenever(appWidgetId).thenReturn(0)
+ whenever(spanX).thenReturn(2)
+ whenever(spanY).thenReturn(1)
+ whenever(options).thenReturn(0)
+ whenever(appWidgetSource).thenReturn(20)
+ whenever(applyCommonProperties(any())).thenCallRealMethod()
+ }
+ mInstallingPkgs = hashMapOf()
+ val inflationResult =
+ WidgetInflater.InflationResult(
+ type = WidgetInflater.TYPE_PENDING,
+ widgetInfo = null
+ )
+ mockWidgetInflater =
+ mock<WidgetInflater>().apply {
+ whenever(inflateAppWidget(any())).thenReturn(inflationResult)
+ }
+ itemProcessorUnderTest =
+ createWorkspaceItemProcessorUnderTest(widgetProvidersMap = mWidgetProvidersMap)
+
+ // When
+ itemProcessorUnderTest.processItem()
+
+ // Then
+ verify(mockCursor).checkAndAddItem(any(), any())
+ } finally {
+ mockitoSession.finishMocking()
+ }
+ }
+
+ private fun createWorkspaceItemProcessorUnderTest(
+ cursor: LoaderCursor = mockCursor,
+ memoryLogger: LoaderMemoryLogger? = null,
+ userCache: UserCache = mockUserCache,
+ userManagerState: UserManagerState = mockUserManagerState,
+ launcherApps: LauncherApps = mockLauncherApps,
+ shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
+ app: LauncherAppState = mockAppState,
+ bgDataModel: BgDataModel = mockBgDataModel,
+ widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
+ widgetInflater: WidgetInflater = mockWidgetInflater,
+ pmHelper: PackageManagerHelper = mockPmHelper,
+ iconRequestInfos: MutableList<IconRequestInfo<WorkspaceItemInfo>> = mIconRequestInfos,
+ isSdCardReady: Boolean = false,
+ pendingPackages: MutableSet<PackageUserKey> = mPendingPackages,
+ unlockedUsers: LongSparseArray<Boolean> = mUnlockedUsersArray,
+ installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo> = mInstallingPkgs,
+ allDeepShortcuts: MutableList<ShortcutInfo> = mAllDeepShortcuts
+ ) =
+ WorkspaceItemProcessor(
+ c = cursor,
+ memoryLogger = memoryLogger,
+ userCache = userCache,
+ userManagerState = userManagerState,
+ launcherApps = launcherApps,
+ app = app,
+ bgDataModel = bgDataModel,
+ widgetProvidersMap = widgetProvidersMap,
+ widgetInflater = widgetInflater,
+ pmHelper = pmHelper,
+ unlockedUsers = unlockedUsers,
+ iconRequestInfos = iconRequestInfos,
+ pendingPackages = pendingPackages,
+ isSdCardReady = isSdCardReady,
+ shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
+ installingPkgs = installingPkgs,
+ allDeepShortcuts = allDeepShortcuts
+ )
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
similarity index 100%
rename from tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
rename to tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 749a75a..68004bb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -351,8 +351,6 @@
/** Waits for setup wizard to go away. */
private static void waitForSetupWizardDismissal() {
- if (!TestStabilityRule.isPresubmit()) return;
-
if (sFirstTimeWaitingForWizard) {
try {
getUiDevice().executeShellCommand(
diff --git a/tests/src/com/android/launcher3/util/RoboApiWrapper.kt b/tests/src/com/android/launcher3/util/RoboApiWrapper.kt
new file mode 100644
index 0000000..583652d
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/RoboApiWrapper.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.util
+
+import android.content.ContentResolver
+import android.net.Uri
+import android.os.Looper
+import java.io.InputStream
+import java.util.function.Supplier
+
+object RoboApiWrapper {
+
+ fun initialize() {}
+
+ fun registerInputStream(
+ contentResolver: ContentResolver,
+ uri: Uri,
+ inputStreamSupplier: Supplier<InputStream>
+ ) {}
+
+ fun waitForLooperSync(looper: Looper) {}
+}
diff --git a/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt b/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt
new file mode 100644
index 0000000..9232268
--- /dev/null
+++ b/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.util
+
+import android.content.ComponentName
+import android.content.ContentResolver
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.ApplicationInfo
+import android.content.pm.LauncherActivityInfo
+import android.content.pm.LauncherApps
+import android.net.Uri
+import android.os.Looper
+import android.os.Process
+import androidx.test.platform.app.InstrumentationRegistry
+import java.io.InputStream
+import java.util.function.Supplier
+import org.mockito.Mockito
+import org.mockito.kotlin.whenever
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.Shadows
+
+object RoboApiWrapper {
+
+ fun initialize() {
+ Shadows.shadowOf(
+ RuntimeEnvironment.getApplication().getSystemService(LauncherApps::class.java)
+ )
+ .addEnabledPackage(
+ Process.myUserHandle(),
+ InstrumentationRegistry.getInstrumentation().context.packageName
+ )
+ LauncherModelHelper.ACTIVITY_LIST.forEach {
+ installApp(ComponentName(InstrumentationRegistry.getInstrumentation().context, it))
+ }
+ }
+
+ private fun installApp(componentName: ComponentName) {
+ val app = RuntimeEnvironment.getApplication()
+ val user = Process.myUserHandle()
+
+ val pm = Shadows.shadowOf(app.packageManager)
+ val ai = pm.addActivityIfNotPresent(componentName)
+ pm.addIntentFilterForActivity(
+ componentName,
+ IntentFilter(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
+ )
+
+ val li = Mockito.mock(LauncherActivityInfo::class.java)
+ val appInfo = ApplicationInfo().apply { flags = 0 }
+ Mockito.doReturn(ai).whenever(li).activityInfo
+ Mockito.doReturn(appInfo).whenever(li).applicationInfo
+ Mockito.doReturn(user).whenever(li).user
+ Mockito.doReturn(1f).whenever(li).loadingProgress
+ Mockito.doReturn(componentName).whenever(li).componentName
+
+ Shadows.shadowOf(app.getSystemService(LauncherApps::class.java)).apply {
+ addActivity(user, li)
+ addEnabledPackage(user, componentName.packageName)
+ setActivityEnabled(user, componentName)
+ addApplicationInfo(user, componentName.packageName, ai.applicationInfo)
+ }
+ }
+
+ fun registerInputStream(
+ contentResolver: ContentResolver,
+ uri: Uri,
+ inputStreamSupplier: Supplier<InputStream>
+ ) {
+ Shadows.shadowOf(contentResolver).registerInputStreamSupplier(uri, inputStreamSupplier)
+ }
+
+ fun waitForLooperSync(looper: Looper) {
+ Shadows.shadowOf(looper).runToEndOfTasks()
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index ac145b7..0edcfea 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -23,11 +23,9 @@
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
-import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH;
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import android.graphics.Rect;
-import android.util.Log;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
@@ -424,31 +422,32 @@
protected boolean isActionsViewVisible() {
if (!hasTasks() || isClearAllVisible()) {
- Log.d(TAG, "Not expecting an actions bar: no tasks/'Clear all' is visible");
+ testLogD(TAG, "Not expecting an actions bar: no tasks/'Clear all' is visible");
return false;
}
boolean isTablet = mLauncher.isTablet();
if (isTablet && mLauncher.isGridOnlyOverviewEnabled()) {
- Log.d(TAG, "Not expecting an actions bar: device is tablet with grid-only Overview");
+ testLogD(TAG, "Not expecting an actions bar: device is tablet with grid-only Overview");
return false;
}
OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask();
if (task == null) {
- Log.d(TAG, "Not expecting an actions bar: no current task");
+ testLogD(TAG, "Not expecting an actions bar: no current task");
return false;
}
// In tablets, if focused task is not in center, overview actions aren't visible.
if (isTablet && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
- Log.d(TAG, "Not expecting an actions bar: device is tablet and task is not centered");
+ testLogD(TAG,
+ "Not expecting an actions bar: device is tablet and task is not centered");
return false;
}
if (task.isTaskSplit() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
- Log.d(TAG, "Not expecting an actions bar: device is phone and task is split");
+ testLogD(TAG, "Not expecting an actions bar: device is phone and task is split");
// Overview actions aren't visible for split screen tasks, except for save app pair
// button on tablets.
return false;
}
- Log.d(TAG, "Expecting an actions bar");
+ testLogD(TAG, "Expecting an actions bar");
return true;
}
@@ -535,13 +534,9 @@
return null;
}
Rect focusTaskSize = mLauncher.getOverviewTaskSize();
- testLogD(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH, "focusTaskSize: " + focusTaskSize);
int focusedTaskHeight = focusTaskSize.height();
for (UiObject2 task : taskViews) {
OverviewTask overviewTask = new OverviewTask(mLauncher, task, this);
-
- testLogD(OVERVIEW_FOCUS_TASK_HEIGHT_MISMATCH,
- "overviewTask.getVisibleHeight(): " + overviewTask.getVisibleHeight());
if (overviewTask.getVisibleHeight() == focusedTaskHeight) {
return overviewTask;
}