Merge "Fix app install icon colors by "reverting" small color change that applies to the private install icon." into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 21b9863..a779641 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -316,3 +316,16 @@
description: "Archived apps will use new icon in app title"
bug: "350758155"
}
+
+flag {
+ name: "enable_multi_instance_menu_taskbar"
+ namespace: "launcher"
+ 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"
+ description: "Settings screen supports navigating to child preference if the key is not on the screen"
+ bug: "293390881"
+}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index f14cebd..1b9c661 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -52,6 +52,14 @@
"tests/src/com/android/quickstep/TaplOverviewIconTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
"tests/src/com/android/quickstep/TaplTestsSplitscreen.java",
- "tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java"
+ "tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java",
+ ],
+}
+
+filegroup {
+ name: "launcher3-quickstep-screenshot-tests-src",
+ path: "tests/multivalentScreenshotTests",
+ srcs: [
+ "tests/multivalentScreenshotTests/src/**/*.kt",
],
}
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 80d8154..c6e2d8c 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -48,7 +48,7 @@
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 4abf6e1..bf198b6 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -80,7 +80,7 @@
android:stateNotNeeded="true"
android:theme="@style/LauncherTheme"
android:screenOrientation="behind"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:enableOnBackInvokedCallback="false"
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index b1fe89e..784a094 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -39,4 +39,16 @@
android:background="@color/overview_foreground_scrim_color"
android:alpha="0" />
+ <FrameLayout
+ android:id="@+id/splash_container"
+ 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>
</com.android.quickstep.task.thumbnail.TaskThumbnailView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 736706a..e8f3d9d 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -35,6 +35,18 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+ <com.android.launcher3.taskbar.bubbles.BubbleBarView
+ android:id="@+id/taskbar_bubbles"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/bubblebar_size_with_pointer"
+ android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin"
+ android:paddingTop="@dimen/bubblebar_pointer_visible_size"
+ android:visibility="gone"
+ android:gravity="center"
+ android:layout_gravity="bottom"
+ android:clipChildren="false"
+ android:elevation="@dimen/bubblebar_elevation" />
+
<com.android.launcher3.taskbar.navbutton.NearestTouchFrame
android:id="@+id/navbuttons_view"
android:layout_width="match_parent"
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 73c8129..f8c8977 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kitsinstellings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taakbalk"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taakbalk word gewys"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taakbalk is versteek"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigasiebalk"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Wys altyd Taakbalk"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Verander navigasiemodus"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Oorvloei"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> vanaf <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> en nog <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Skuif links"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Skuif regs"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Maak almal toe"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"vou <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> uit"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"vou <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> in"</string>
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index f9eed39..f5cef7b 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ፈጣን ቅንብሮች"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"የተግባር አሞሌ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"የተግባር አሞሌ ይታያል"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"የተግባር አሞሌ ተደብቋል"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"የአሰሳ አሞሌ"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ሁልጊዜ የተግባር አሞሌ ያሳዩ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"የአሰሳ ሁነታን ይለውጡ"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ትርፍ ፍሰት"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ከ<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> እና <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> ተጨማሪ"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ወደ ግራ ያንቀሳቅሱ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ወደ ቀኝ ያንቀሳቅሱ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ሁሉንም አሰናብት"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ን ዘርጋ"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ን ሰብስብ"</string>
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 7a0be9b..52a18e9 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"إعدادات سريعة"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"شريط التطبيقات"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"تم إظهار شريط التطبيقات"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"تم إخفاء شريط التطبيقات"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"شريط التنقل"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"إظهار شريط التطبيقات دائمًا"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"تغيير وضع التنقل"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"القائمة الكاملة"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"\"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" من \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"\"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>\" و<xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> غيرها"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"نقل لليسار"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"نقل لليمين"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"إغلاق الكل"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"توسيع <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"تصغير <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index d440400..d811543 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ক্ষিপ্ৰ ছেটিং"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"টাস্কবাৰ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"টাস্কবাৰ দেখুওৱা হৈছে"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"টাস্কবাৰ আৰু বাবল বাওঁফালে দেখুওৱা হৈছে"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"টাস্কবাৰ আৰু বাবল সোঁফালে দেখুওৱা হৈছে"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"টাস্কবাৰ লুকুৱাই থোৱা হৈছে"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"টাস্কবাৰ আৰু বাবল লুকুওৱা হৈছে"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"নেভিগেশ্বনৰ দণ্ড"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"টাস্কবাৰ সদায় দেখুৱাওক"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"নেভিগেশ্বন ম’ড সলনি কৰক"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"অ’ভাৰফ্ল’"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> আৰু <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> টা"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"বাওঁফাললৈ নিয়ক"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"সোঁফাললৈ নিয়ক"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"সকলো অগ্ৰাহ্য কৰক"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> বিস্তাৰ কৰক"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> সংকোচন কৰক"</string>
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 9cdcc01..3108ffe 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Sürətli Ayarlar"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Tapşırıq paneli"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"İşləmə paneli göstərilir"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"İşləmə paneli gizlədilib"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Naviqasiya paneli"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"İşləmə paneli həmişə görünsün"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Naviqasiya rejimini dəyişin"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kənara çıxma"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> və daha <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> yumrucuq"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Sola köçürün"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Sağa köçürün"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hamısını kənarlaşdırın"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"genişləndirin: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"yığcamlaşdırın: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index b6271a9..e6074b0 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Brza podešavanja"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Traka zadataka"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Traka zadataka je prikazana"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Traka zadataka je skrivena"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Traka za navigaciju"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Uvek prikazuj traku zadataka"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Promeni režim navigacije"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Preklopni"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Pomeri nalevo"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Pomeri nadesno"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Odbaci sve"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skupite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index bb6c764..5b871d4 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Хуткія налады"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Панэль задач"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Панэль задач паказана"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Панэль задач схавана"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панэль навігацыі"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Заўсёды паказваць панэль задач"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Змяніць рэжым навігацыі"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Меню з пашырэннем"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, крыніца: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> і яшчэ <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Перамясціць улева"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Перамясціць управа"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Закрыць усе"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: разгарнуць"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: згарнуць"</string>
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index d674dbc..dd87de8 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Бързи настройки"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Лента на задачите"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Лентата на задачите се показва"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Лентата на задачите е скрита"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Лента за навигация"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Лентата на задачите винаги да се показва"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Промяна на режима на навигация"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Препълване"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> от <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> и още <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Преместване наляво"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Преместване надясно"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Отхвърляне на всички"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"разгъване на <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"свиване на <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 3e974f5..99a815f 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"দ্রুত সেটিংস"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"টাস্কবার"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"\'টাস্কবার\' দেখানো হয়েছে"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"\'টাস্কবার\' লুকানো রয়েছে"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"নেভিগেশন বার"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"সবসময় টাস্কবার দেখুন"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"\'নেভিগেশন\' মোড পরিবর্তন করুন"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ওভারফ্লো"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> থেকে <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> এবং আরও <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>টি"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"বাঁদিকে সরান"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ডানদিকে সরান"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"সব বাতিল করুন"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> বড় করুন"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> আড়াল করুন"</string>
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 19268be..5f445ab 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Brze postavke"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Traka zadataka"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Traka zadataka je prikazana"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Traka zadataka je sakrivena"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigaciona traka"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Uvijek prikaži traku zadataka"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Promijeni način navigacije"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Preklopni meni"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Pomjeranje ulijevo"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Pomjeranje udesno"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Odbacivanje svega"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširivanje oblačića <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sužavanje oblačića <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 6a6f131..2b8fadf 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. ràpida"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tasques"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Es mostra la Barra de tasques"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"S\'ha amagat la Barra de tasques"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegació"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tasques sempre visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Canvia el mode de navegació"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Desbordament"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> i <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> més"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mou cap a l\'esquerra"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mou cap a la dreta"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ignora-ho tot"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"desplega <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"replega <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index e134850..6e432de 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rychlé nastavení"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Panel aplikací"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Panel aplikací je zobrazen"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Panel aplikací je skrytý"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigační panel"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Vždy zobrazovat panel aplikací"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Změnit režim navigace"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozbalovací nabídka"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikace <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> a ještě <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Přesunout doleva"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Přesunout doprava"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Zavřít vše"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozbalit <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sbalit <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index e9cdbce..c73f49c 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kvikmenu"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Proceslinje"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Proceslinjen vises"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Proceslinjen er skjult"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigationslinje"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Vis altid proceslinjen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Skift navigationstilstand"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overløb"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> mere"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Flyt til venstre"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Flyt til højre"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Afvis alle"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"udvid <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skjul <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 9e5cb12..8f571ce 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Schnelleinstellungen"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskleiste"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskleiste eingeblendet"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskleiste ausgeblendet"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigationsleiste"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Taskleiste immer anzeigen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigationsmodus ändern"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Weitere Optionen"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ aus <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> und <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> weitere"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Nach links bewegen"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Nach rechts bewegen"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Alle schließen"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"„<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“ maximieren"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"„<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“ minimieren"</string>
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index bbb1282..011c800 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Γρήγορες ρυθμ."</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Γραμμή εργαλείων"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Η γραμμή εργαλείων εμφανίζεται"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Η γραμμή εργαλείων είναι κρυφή"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Γραμμή πλοήγησης"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Εμφάνιση Γραμμής εργαλείων"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Αλλαγή τρόπου πλοήγησης"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Υπερχείλιση"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> από <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> και <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> ακόμα"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Μετακίνηση αριστερά"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Μετακίνηση δεξιά"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Παράβλεψη όλων"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ανάπτυξη <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"σύμπτυξη <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index b84f646..c63e094 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> more"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Move left"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Move right"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 88cd0dd..2183919 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"Taskbar & bubbles left shown"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"Taskbar & bubbles right shown"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"Taskbar & bubbles hidden"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> more"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Move left"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Move right"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index b84f646..c63e094 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> more"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Move left"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Move right"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index b84f646..c63e094 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> more"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Move left"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Move right"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index 76dab0d..ba55585 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar shown"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"Taskbar & bubbles left shown"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"Taskbar & bubbles right shown"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar hidden"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"Taskbar & bubbles hidden"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> more"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Move left"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Move right"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dismiss all"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expand <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"collapse <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 58d540f..7df97ef 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. rápida"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tareas"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ampliada"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> y <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> más"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mover hacia la izquierda"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mover hacia la derecha"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Descartar todo"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expandir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 2a956f0..353ff4c 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ajustes rápidos"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tareas"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menú adicional"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> y <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> más"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mover hacia la izquierda"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mover hacia la derecha"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Cerrar todo"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"desplegar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 0ecc0c0..6b78c1f 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Kiirseaded"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Tegumiriba"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Tegumiriba on kuvatud"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Tegumiriba on peidetud"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigeerimisriba"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Kuva tegumiriba alati"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigeerimisrežiimi muutmine"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ületäide"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ja veel <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> mulli"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Liigu vasakule"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Liigu paremale"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Loobu kõigist"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Toiminguriba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> laiendamine"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Toiminguriba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ahendamine"</string>
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index e83ee28..058ade5 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ezarpen bizkorrak"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Zereginen barra"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Zereginen barra ikusgai dago"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Zereginen barra itxita dago"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Nabigazio-barra"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Erakutsi beti zereginen barra"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Aldatu nabigazio modua"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Luzapena"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> eta beste <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Eraman ezkerrera"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Eraman eskuinera"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Baztertu guztiak"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"zabaldu <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"tolestu <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index bafc2d5..f6f51b7 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"تنظیمات فوری"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"نوار وظیفه"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"نوار وظیفه نمایان است"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"نوار وظیفه پنهان است"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"نوار پیمایش"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"نوار وظیفه همیشه نشان داده شود"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"تغییر حالت پیمایش"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"سرریز"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> و <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> حبابک دیگر"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"انتقال به چپ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"انتقال به راست"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"رد کردن همه"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ازهم باز کردن <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"جمع کردن <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 5ac124a..91f64ed 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Pika-asetukset"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Tehtäväpalkki"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Tehtäväpalkki näkyvissä"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Tehtäväpalkki piilotettu"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigointipalkki"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Näytä tehtäväpalkki aina"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Vaihda navigointitilaa"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ylivuoto"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ja <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> muuta"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Siirrä vasemmalle"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Siirrä oikealle"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hylkää kaikki"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"laajenna <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"tiivistä <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 9510494..e567239 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Paramètres rapides"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barre des tâches"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barre des tâches affichée"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barre des tâches masquée"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barre de navigation"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Tjrs afficher barre des tâches"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Changer de mode de navigation"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bulle à développer"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> autres"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Déplacer vers la gauche"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Déplacer vers la droite"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tout ignorer"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Développer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Réduire <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 60f8944..ddab232 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Réglages rapides"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barre des tâches"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barre des tâches affichée"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barre des tâches masquée"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barre de navigation"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Barre des tâches tjs visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Modifier le mode de navigation"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dépassement"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> autre(s)"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Déplacer vers la gauche"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Déplacer vers la droite"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tout fermer"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Développer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Réduire <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index bf081d4..053a48f 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Configuración rápida"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Estase mostrando a barra de tarefas"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Non se está mostrando a barra de tarefas"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Ver sempre a barra de tarefas"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar modo de navegación"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menú adicional"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> e <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> máis"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mover cara á esquerda"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mover cara á dereita"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Pechar todo"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"despregar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"contraer <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index b20f771..a081ab5 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ઝડપી સેટિંગ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ટાસ્કબાર"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ટાસ્કબાર બતાવવામાં આવ્યો"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ટાસ્કબાર છુપાવવામાં આવ્યો"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"નૅવિગેશન બાર"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"હંમેશાં ટાસ્કબાર બતાવો"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"નૅવિગેશન મોડ બદલો"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ઓવરફ્લો"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>થી <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> અને વધુ <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ડાબે ખસેડો"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"જમણે ખસેડો"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"તમામ છોડી દો"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> મોટો કરો"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> નાનો કરો"</string>
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index a645186..1e74c4a 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"फटाफट सेटिंग"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार दिखाया गया"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"टास्कबार छिपाया गया"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेविगेशन बार"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"टास्कबार हमेशा दिखाएं"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"नेविगेशन का मोड बदलें"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओवरफ़्लो"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> की <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> वाली सूचना"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> और <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> अन्य"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"बाईं ओर ले जाएं"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"दाईं ओर ले जाएं"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"सभी खारिज करें"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> को बड़ा करें"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> को छोटा करें"</string>
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index c96381d..fc85be9 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Brze postavke"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Traka sa zadacima"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Traka sa zadacima prikazana"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Traka sa zadacima skrivena"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigacijska traka"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Uvijek prikaži traku zadataka"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Promijeni način navigacije"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dodatni izbornik"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Pomakni ulijevo"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Pomakni udesno"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Odbaci sve"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"proširite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sažmite oblačić <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 03235aa..3a539d4 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Gyorsbeállítások"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Tálca"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Feladatsáv megjelenítve"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Feladatsáv elrejtve"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigációs sáv"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Mindig megjelenő Feladatsáv"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigációs mód módosítása"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Túlcsordulás"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, forrás: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> és <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> további"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mozgatás balra"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mozgatás jobbra"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Az összes elvetése"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> kibontása"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> összecsukása"</string>
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 3cb7990..d8503f3 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Արագ կարգավորումներ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Խնդրագոտի"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Խնդրագոտին ցուցադրվում է"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Խնդրագոտին թաքցված է"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Նավիգացիայի գոտի"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Միշտ ցույց տալ վահանակը"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Փոխել նավիգացիայի ռեժիմը"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Լրացուցիչ ընտրացանկ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածից"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ու ևս <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> ամպիկ"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Տեղափոխել ձախ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Տեղափոխել աջ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Փակել բոլորը"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>. ծավալել"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>. ծալել"</string>
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 015b09e..d4def8d 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Setelan Cepat"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar ditampilkan"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar disembunyikan"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Menu navigasi"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Selalu tampilkan Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Ubah mode navigasi"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Tambahan"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> dari <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> dan <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> lainnya"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Pindahkan ke kiri"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Pindahkan ke kanan"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tutup semua"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"luaskan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ciutkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 883fe82..f2dd60c 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Flýtistillingar"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Verkstika"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Forritastika sýnd"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Forritastika falin"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Yfirlitsstika"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Alltaf sýna forritastiku"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Breyta leiðsagnarstillingu"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Yfirflæði"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> frá <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> í viðbót"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Færa til vinstri"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Færa til hægri"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hunsa allt"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"stækka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"minnka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 59b195a..9ed28c1 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Impostazioni rapide"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra delle applicazioni"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra delle app visualizzata"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"Barra app e bolle most. sinis."</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"Barra app e bolle most. destr."</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra delle app nascosta"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"Barra app e bolle nascoste"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra di navigazione"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Mostra sempre barra app"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambia modalità di navigazione"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Extra"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> e altri <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Sposta a sinistra"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Sposta a destra"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ignora tutte"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"espandi <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"comprimi <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 9f0ed14..da8b37d 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"הגדרות מהירות"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"סרגל האפליקציות"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"סרגל האפליקציות מוצג"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"סרגל האפליקציות מוסתר"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"סרגל הניווט"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"סרגל האפליקציות מוצג תמיד"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"שינוי מצב הניווט"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"אפשרויות נוספות"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מתוך <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"הזזה שמאלה"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"הזזה ימינה"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ביטול של הכול"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"הרחבה של <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"כיווץ של <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 4f1a162..54f51da 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"クイック設定"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"タスクバー"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"タスクバー表示"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"タスクバーとバブルを表示"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"タスクバーとバブルを右側に表示"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"タスクバー非表示"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"タスクバーとバブルを非表示"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ナビゲーション バー"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"常にタスクバーを表示する"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ナビゲーション モードを変更"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"オーバーフロー"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>(<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>、他 <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> 件"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"左に移動"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"右に移動"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"すべて解除"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>を開きます"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>を閉じます"</string>
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 1fb6077..5328012 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"სწრაფი პარამეტრები"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ამოცანათა ზოლი"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ამოცანათა ზოლი ნაჩვენებია"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ამოცანათა ზოლი დამალულია"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ნავიგაციის ზოლი"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ამოცანათა ზოლის მუდამ ჩვენება"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"შეცვალეთ ნავიგაციის რეჟიმი"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"გადავსება"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: <xliff:g id="APP_NAME">%2$s</xliff:g>-იდან"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> და <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> სხვა"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"მარცხნივ გადატანა"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"მარჯვნივ გადატანა"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ყველას დახურვა"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-ის გაფართოება"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-ის ჩაკეცვა"</string>
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index d83e2d3..54c1795 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Жылдам параметрлер"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Тапсырмалар жолағы"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Тапсырмалар жолағы көрсетілді"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Тапсырмалар жолағы жасырылды"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Навигация жолағы"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Тапсырма жолағын үнемі көрсету"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Навигация режимін өзгерту"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Қосымша мәзір"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> ұсынатын <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> және тағы <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Солға жылжыту"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Оңға жылжыту"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Барлығын жабу"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: жаю"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>: жию"</string>
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 5448433..698f33c 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ការកំណត់រហ័ស"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"របារកិច្ចការ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"បានបង្ហាញរបារកិច្ចការ"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"បានលាក់របារកិច្ចការ"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"របាររុករក"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"បង្ហាញរបារកិច្ចការជានិច្ច"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ប្ដូរមុខងាររុករក"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ម៉ឺនុយបន្ថែម"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ពី <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> និង <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> នាក់ទៀត"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ផ្លាស់ទីទៅឆ្វេង"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ផ្លាស់ទីទៅស្ដាំ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ច្រានចោលទាំងអស់"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ពង្រីក <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"បង្រួម <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 74c7750..6a86e38 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ಟಾಸ್ಕ್ಬಾರ್"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ಟಾಸ್ಕ್ಬಾರ್ ತೋರಿಸಲಾಗಿದೆ"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ಟಾಸ್ಕ್ಬಾರ್ ಮರೆಮಾಡಲಾಗಿದೆ"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ಯಾವಾಗಲೂ ಟಾಸ್ಕ್ಬಾರ್ ತೋರಿಸಿ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ನ್ಯಾವಿಗೇಶನ್ ಮೋಡ್ ಬದಲಾಯಿಸಿ"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ಓವರ್ಫ್ಲೋ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> ನಿಂದ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ಮತ್ತು ಇನ್ನೂ <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ಎಲ್ಲವನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index c27b7f8..31942fb 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"빠른 설정"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"태스크 바"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"태스크 바 표시"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"태스크 바 숨김"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"탐색 메뉴"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"태스크 바 항상 표시"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"탐색 모드 변경"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"더보기"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>의 <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> 외 <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>개"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"왼쪽으로 이동"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"오른쪽으로 이동"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"모두 닫기"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> 펼치기"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> 접기"</string>
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index f0d2af8..8805bd0 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ыкчам параметрлер"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Тапшырмалар тактасы"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Тапшырмалар панели көрсөтүлдү"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Тапшырмалар панели жашырылды"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Чабыттоо тилкеси"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Такта ар дайым көрүнсүн"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Өтүү режимин өзгөртүү"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Кошумча меню"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосунан <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> жана дагы <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Солго жылдыруу"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Оңго жылдыруу"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Баарын четке кагуу"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> жайып көрсөтүү"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> жыйыштыруу"</string>
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index f54c712..9e70a5c 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ການຕັ້ງຄ່າດ່ວນ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ແຖບໜ້າວຽກ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ແຖບໜ້າວຽກທີ່ສະແດງຢູ່"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ແຖບໜ້າວຽກທີ່ເຊື່ອງໄວ້ຢູ່"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ແຖບການນຳທາງ"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ສະແດງແຖບໜ້າວຽກສະເໝີ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ປ່ຽນໂໝດການນຳທາງ"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ລາຍການເພີ່ມເຕີມ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ຈາກ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ແລະ ອີກ <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> ລາຍການ"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ຍ້າຍໄປຊ້າຍ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ຍ້າຍໄປຂວາ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ປິດທັງໝົດ"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ຂະຫຍາຍ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ຫຍໍ້ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ລົງ"</string>
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 554745e..f78fa30 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Spartieji nustatymai"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Užduočių juosta"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Užduočių juosta rodoma"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Užduočių juosta paslėpta"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Naršymo juosta"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Visada rodyti užduočių juostą"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Keisti naršymo režimą"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Perpildymas"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ iš „<xliff:g id="APP_NAME">%2$s</xliff:g>“"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"„<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>“ ir dar <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Perkelti kairėn"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Perkelti dešinėn"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Atsisakyti visų"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"išskleisti „<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sutraukti „<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>“"</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index a6a0dab..799e8d1 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Ātrie iestatīj."</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Uzdevumu josla"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Uzdevumu josla tiek rādīta"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Uzdevumu josla paslēpta"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigācijas josla"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Vienmēr rādīt uzdevumu joslu"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Mainīt navigācijas režīmu"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Pārpilde"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> no lietotnes <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> un vēl <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Pārvietot pa kreisi"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Pārvietot pa labi"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Nerādīt nevienu"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"izvērst “<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"sakļaut “<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 4859055..04f35ba 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Брзи поставки"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Лента со задачи"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Лентата со задачи е прикажана"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Лентата со задачи е скриена"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Лента за навигација"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Секогаш прикажувај „Лента“"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Променете режим на навигација"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Проширено балонче"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> од <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> и уште <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Премести налево"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Премести надесно"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Отфрли ги сите"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"прошири <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"собери <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 85b093d..0db3e1f 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ദ്രുത ക്രമീകരണം"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ടാസ്ക്ബാർ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ടാസ്ക്ബാർ കാണിച്ചിരിക്കുന്നു"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ടാസ്ക്ബാർ മറച്ചിരിക്കുന്നു"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"നാവിഗേഷൻ ബാർ"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ടാസ്ക്ബാർ എപ്പോഴും കാണിക്കൂ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"നാവിഗേഷൻ മോഡ് മാറ്റുക"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ഓവർഫ്ലോ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> എന്നതും മറ്റ് <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> എണ്ണവും"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"വലത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"എല്ലാം ഡിസ്മിസ് ചെയ്യുക"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> വികസിപ്പിക്കുക"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ചുരുക്കുക"</string>
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index fe2e4a4..a76117a 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Шуурхай тохиргоо"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Ажлын хэсэг"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Ажлын хэсгийг харуулсан"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Ажлын хэсгийг нуусан"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Навигацын самбар"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Ажлын хэсгийг үргэлж харуулах"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Навигацын горимыг өөрчлөх"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Илүү хэсэг"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>-с ирсэн <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> болон бусад <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Зүүн тийш зөөх"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Баруун тийш зөөх"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Бүгдийг үл хэрэгсэх"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-г дэлгэх"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>-г хураах"</string>
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index b053a21..09f40ab 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"क्विक सेटिंग्ज"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार दाखवलेला आहे"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"टास्कबार लपवलेले आहे"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेव्हिगेशन बार"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"नेहमी टास्कबार दाखवा"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"नेव्हिगेशन मोड बदला"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओव्हरफ्लो"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> वरील <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> आणि आणखी <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"डावीकडे हलवा"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"उजवीकडे हलवा"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"सर्व डिसमिस करा"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> चा विस्तार करा"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> कोलॅप्स करा"</string>
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index c0219e0..72887ef 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Tetapan Pantas"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Bar Tugas"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Bar Tugas dipaparkan"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"Bar Tugas & gelembung dipaparkan di sebelah kiri"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"Bar Tugas & gelembung dipaparkan di sebelah kanan"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Bar Tugas disembunyikan"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"Bar Tugas & gelembung disembunyikan"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Bar navigasi"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Papar Bar Tugas selalu"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Tukar mod navigasi"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Limpahan"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> daripada <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> dan <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> lagi"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Alih ke kiri"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Alih ke kanan"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ketepikan semua"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"kembangkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"kuncupkan <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 7c7ff82..749c1b5 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"အမြန်ဆက်တင်များ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"လုပ်ဆောင်စရာဘား"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar ပြထားသည်"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar ဖျောက်ထားသည်"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"လမ်းညွှန်ဘား"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Taskbar အမြဲပြရန်"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ရွှေ့ကြည့်သည့်မုဒ် ပြောင်းရန်"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"မီနူးအပို"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> မှ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> နှင့် နောက်ထပ် <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> ခု"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ဘယ်သို့ရွှေ့ရန်"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ညာသို့ရွှေ့ရန်"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"အားလုံးကို ပယ်ရန်"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ကို ပိုပြပါ"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ကို လျှော့ပြပါ"</string>
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 6aa755a..245945b 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hurtiginnst."</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Oppgavelinje"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Oppgavelinjen vises"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Oppgavelinjen er skjult"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigasjonsrad"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Vis alltid oppgavelinjen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Endre navigasjonsmodus"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflyt"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> andre"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Flytt til venstre"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Flytt til høyre"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Lukk alle"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"vis <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"skjul <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index d49fd2d..f881637 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"द्रुत सेटिङ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"टास्कबार"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"टास्कबार देखाइएको छ"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"टास्कबार लुकाइएको छ"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"नेभिगेसन बार"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"टास्कबार सधैँ देखाउनुहोस्"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"नेभिगेसन मोड बदल्नुहोस्"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओभरफ्लो"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> मा देखाइएका <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> र थप <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"बायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"दायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"सबै हटाउनुहोस्"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index ca44a69..09f8aca 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Snelle instellingen"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taakbalk"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taakbalk wordt getoond"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taakbalk is verborgen"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigatiebalk"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Taakbalk altijd tonen"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigatiemodus wijzigen"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overloop"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> en nog <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Naar links verplaatsen"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Naar rechts verplaatsen"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Alles sluiten"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> uitvouwen"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> samenvouwen"</string>
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index bf0bdc8..83522f3 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"କୁଇକ ସେଟିଂସ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ଟାସ୍କବାର"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ଟାସ୍କବାର ଦେଖାଯାଇଛି"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ଟାସ୍କବାର ଲୁଚାଯାଇଛି"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ନାଭିଗେସନ ବାର"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ସର୍ବଦା ଟାସ୍କବାର ଦେଖାନ୍ତୁ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ନାଭିଗେସନ ମୋଡ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ଓଭରଫ୍ଲୋ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g>ରୁ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ଏବଂ ଅଧିକ <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ସବୁ ଖାରଜ କରନ୍ତୁ"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index fc60396..197fc6c 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ਟਾਸਕਬਾਰ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ਟਾਸਕਬਾਰ ਨੂੰ ਦਿਖਾਇਆ ਗਿਆ"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ਟਾਸਕਬਾਰ ਨੂੰ ਲੁਕਾਇਆ ਗਿਆ"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"ਨੈਵੀਗੇਸ਼ਨ ਵਾਲੀ ਪੱਟੀ"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ਹਮੇਸ਼ਾਂ ਟਾਸਕਬਾਰ ਦਿਖਾਓ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ਨੈਵੀਗੇਸ਼ਨ ਮੋਡ ਬਦਲੋ"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ਓਵਰਫ਼ਲੋ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਤੋਂ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ਅਤੇ <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> ਹੋਰ"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ਸਭ ਖਾਰਜ ਕਰੋ"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index d88e28a..5d6d927 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -91,7 +91,7 @@
<string name="allset_hint" msgid="459504134589971527">"Aby przejść na stronę główną, przesuń w górę"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Kliknij przycisk ekranu głównego, aby otworzyć ekran główny"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> jest gotowe – możesz zacząć z niego korzystać"</string>
- <string name="default_device_name" msgid="6660656727127422487">"urządzenie"</string>
+ <string name="default_device_name" msgid="6660656727127422487">"Urządzenie"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Ustawienia nawigacji w systemie"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</string>
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Szybkie ustawienia"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Pasek aplikacji"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Pasek aplikacji widoczny"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Pasek aplikacji ukryty"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Pasek nawigacyjny"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Zawsze pokazuj pasek aplikacji"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Zmień tryb nawigacji"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozwijany"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> i jeszcze <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Przenieś w lewo"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Przenieś w prawo"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Zamknij wszystkie"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozwiń dymek: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"zwiń dymek: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index e4d07bd..4787ec3 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Definiç. rápidas"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tarefas apresentada"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tarefas ocultada"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Ver sempre Barra de tarefas"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Alterar modo de navegação"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menu adicional"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da app <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> e mais <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> pessoas"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mover para a esquerda"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mover para a direita"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ignorar tudo"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"expandir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"reduzir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 4fec4f8..cb45f07 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Config. rápidas"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Barra de tarefas"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tarefas visível"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tarefas oculta"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegação"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Sempre mostrar a Barra de tarefas"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Mudar o modo de navegação"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Balão flutuante"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> do app <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> e mais <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Mover para esquerda"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Mover para direita"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Dispensar todos"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"abrir <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"fechar <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index c839602..31b1951 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Setări rapide"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Bară de activități"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Bara de activități este afișată"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Bara de activități este ascunsă"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Bară de navigare"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Afișează mereu bara"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Schimbă modul de navigare"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Suplimentar"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Deplasează spre stânga"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Deplasează spre dreapta"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Închide-le pe toate"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"extinde <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"restrânge <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index da49ad3..72559f6 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Быстрые настройки"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Панель задач"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Панель задач показана"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Панель задач скрыта"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навигации"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Всегда показывать панель задач"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Изменить режим навигации"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Дополнительное меню"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"\"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" из приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> и ещё <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Переместить влево"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Переместить вправо"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Закрыть все"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"Развернуто: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"Свернуто: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 9cbe837..6e7aabb 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"ඉක්මන් සැකසීම්"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"කාර්ය තීරුව"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"කාර්ය තීරුව පෙන්වා ඇත"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"කාර්ය තීරුව සඟවා ඇත"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"සංචලන තීරුව"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"සෑම විටම කාර්ය තීරුව පෙන්වන්න"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"සංචාලන ප්රකාරය වෙනස් කරන්න"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"පිටාර යාම"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> සිට <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> හා තව <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>ක්"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"වමට ගෙන යන්න"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"දකුණට ගෙන යන්න"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"සියල්ල ඉවතලන්න"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> දිග හරින්න"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> හකුළන්න"</string>
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 3eca787..5fbe4f1 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Rýchle nastavenia"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Panel aplikácií"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Panel aplikácií je zobrazený"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Panel aplikácií je skrytý"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigačný panel"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Zobrazovať panel aplikácií"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Zmeniť režim navigácie"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozbaľovacia ponuka"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> a ešte <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Posunúť doľava"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Posunúť doprava"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Zavrieť všetko"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"rozbaliť <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"zbaliť <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 52faeb7..62c0c3c 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hitre nastavitve"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Opravilna vrstica"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Opravilna vrstica je prikazana"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Opravilna vrstica je skrita"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Vrstica za krmarjenje"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Stalen prikaz oprav. vrstice"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Spreminjanje načina navigacije"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Oblaček z dodatnimi elementi"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> in še <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Premik v levo"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Premik v desno"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Opusti vse"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"razširitev oblačka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"strnitev oblačka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index cdb9cf9..78f2733 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cilësimet shpejt"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Shiriti i detyrave"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Shiriti i detyrave i shfaqur"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Shiriti i detyrave i fshehur"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Shiriti i navigimit"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Shfaq gjithmonë shiritin e detyrave"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Ndrysho modalitetin e navigimit"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Tejkalimi"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"\"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" nga <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"\"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>\" dhe <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> të tjera"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Lëviz majtas"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Lëviz djathtas"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hiqi të gjitha"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"zgjero <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"palos <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 7456a36..ee3a467 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Брза подешавања"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Трака задатака"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Трака задатака је приказана"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Трака задатака је скривена"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Трака за навигацију"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Увек приказуј траку задатака"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Промени режим навигације"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Преклопни"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> и још <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Помери налево"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Помери надесно"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Одбаци све"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"проширите облачић <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"скупите облачић <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index f369dae..e2cf71f 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Snabbinställn."</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Aktivitetsfält"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Aktivitetsfältet visas"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Aktivitetsfältet är dolt"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigeringsfält"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Visa alltid aktivitetsfältet"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Ändra navigeringsläge"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Fler alternativ"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> från <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> och <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> till"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Flytta åt vänster"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Flytta åt höger"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Stäng alla"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"utöka <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"komprimera <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 3d8277b..66feb58 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Mipangilio ya Haraka"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Upauzana"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Upauzana umeonyeshwa"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Upauzana umefichwa"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Sehemu ya viungo muhimu"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Onyesha Zana kila wakati"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Badilisha hali ya usogezaji"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kiputo cha vipengee vya ziada"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kutoka <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> na vingine <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Sogeza kushoto"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Sogeza kulia"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Ondoa vyote"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"panua <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"kunja <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 47d8055..43e12ee 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"விரைவு அமைப்புகள்"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"செயல் பட்டி"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"செயல் பட்டி காட்டப்படுகிறது"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"செயல் பட்டி மறைக்கப்பட்டுள்ளது"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"வழிசெலுத்தல் பட்டி"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"செயல் பட்டியை எப்போதும் காட்டு"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"வழிசெலுத்தல் பயன்முறையை மாற்று"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"கூடுதல் விருப்பங்களைக் காட்டும்"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> வழங்கும் <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> மற்றும் <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"இடதுபுறம் நகர்த்தும்"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"வலதுபுறம் நகர்த்தும்"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"அனைத்தையும் மூடும்"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ஐ விரிவாக்கும்"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index a4e1cbf..fbe67dc 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -130,7 +130,10 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"క్విక్ సెట్టింగ్లు"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"టాస్క్బార్"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"టాస్క్బార్ చూపబడింది"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_left_title" msgid="4242431789851790046">"టాస్క్బార్, బబుల్స్ ఎడమవైపున చూపబడ్డాయి"</string>
+ <string name="taskbar_a11y_shown_with_bubbles_right_title" msgid="8219065376188180113">"టాస్క్బార్, బబుల్స్ కుడివైపున చూపబడ్డాయి"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"టాస్క్బార్ దాచబడింది"</string>
+ <string name="taskbar_a11y_hidden_with_bubbles_title" msgid="7397395993149508087">"టాస్క్బార్, బబుల్స్ దాచబడినవి"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"నావిగేషన్ బార్"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"టాస్క్బార్ను నిరంతరం చూపండి"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"నావిగేషన్ మోడ్ను మార్చండి"</string>
@@ -144,4 +147,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ఓవర్ఫ్లో"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>, మరో <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ఎడమ వైపుగా జరపండి"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"కుడి వైపుగా జరపండి"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"అన్నింటినీ విస్మరించండి"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ను విస్తరించండి"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ను కుదించండి"</string>
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 1bbb137..7816628 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"การตั้งค่าด่วน"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"แถบงาน"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"แถบงานแสดงอยู่"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"แถบงานซ่อนอยู่"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"แถบนำทาง"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"แสดงแถบงานเสมอ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"เปลี่ยนโหมดการนําทาง"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"การดำเนินการเพิ่มเติม"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> จาก <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> และอีก <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> รายการ"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"ย้ายไปทางซ้าย"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"ย้ายไปทางขวา"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"ปิดทั้งหมด"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"ขยาย <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"ยุบ <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 978a5a3..e1a77a5 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Quick Settings"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Ipinapakita ang taskbar"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Nakatago ang taskbar"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigation bar"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Ipakita lagi ang Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Magpalit ng navigation mode"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> mula sa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> at <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> pa"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Ilipat pakaliwa"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Ilipat pakanan"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"I-dismiss lahat"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"i-expand ang <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"i-collapse ang <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 0cc5d7f..ef8f50d 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Hızlı Ayarlar"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Görev çubuğu."</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Görev çubuğu gösteriliyor"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Görev çubuğu gizlendi"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Gezinme çubuğu"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Görev çubuğunu daima göster"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Gezinme modunu değiştir"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Taşma"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> uygulamasından <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> ve <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> tane daha"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Sola taşı"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Sağa taşı"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Tümünü kapat"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"genişlet: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"daralt: <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 9c706a8..201ac18 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Швидкі налаштув."</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Панель завдань"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Панель завдань показано"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Панель завдань приховано"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Панель навігації"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Завжди показув. панель завдань"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Змінити режим навігації"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Додаткове повідомлення"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> з додатка <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> і ще <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Перемістити вліво"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Перемістити вправо"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Закрити все"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"розгорнути \"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>\""</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"згорнути \"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>\""</string>
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index e125248..b2a5c88 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"فوری ترتیبات"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"ٹاسک بار"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"ٹاشک بار دکھایا گیا"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"ٹاسک بار چھپایا گیا"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"نیویگیشن بار"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"ہمیشہ ٹاسک بار دکھائیں"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"نیویگیشن موڈ تبدیل کریں"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"اوورفلو"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> سے <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> اور <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> مزید"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"بائیں منتقل کریں"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"دائیں منتقل کریں"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"سبھی کو برخاست کریں"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> کو پھیلائیں"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g> کو سکیڑیں"</string>
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 3f4f981..6df0b31 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Tezkor sozlamalar"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Vazifalar paneli"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Vazifalar paneli ochiq"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Vazifalar paneli yopiq"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Navigatsiya paneli"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Vazifalar paneli doim chiqarilsin"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Navigatsiya rejimini oʻzgartirish"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kengaytirish"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> va yana <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> kishi"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Chapga siljitish"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Oʻngga siljitish"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Hammasini yopish"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ni yoyish"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>ni yigʻish"</string>
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 9bc526f..76eec0c 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Cài đặt nhanh"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"Thanh tác vụ"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Đã hiện thanh thao tác"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Đã ẩn thanh thao tác"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Thanh điều hướng"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Luôn hiện Thanh tác vụ"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Thay đổi chế độ điều hướng"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bong bóng bổ sung"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> từ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> và <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> bong bóng khác"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Di chuyển sang trái"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Di chuyển sang phải"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Đóng tất cả"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"mở rộng <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"thu gọn <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 79ea299..b5ae26d 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"快捷设置"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"任务栏"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"任务栏已显示"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"任务栏已隐藏"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"导航栏"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"始终显示任务栏"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"更改导航模式"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢出式气泡框"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"来自“<xliff:g id="APP_NAME">%2$s</xliff:g>”的<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>以及另外 <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> 个"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"左移"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"右移"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"全部关闭"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"展开“<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收起“<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>”"</string>
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index b9d8eb7..dd52065 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"工作列"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"顯示咗工作列"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"隱藏咗工作列"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"導覽列"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"一律顯示工作列"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"變更導覽模式"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"展開式"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="APP_NAME">%2$s</xliff:g> 的「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」通知"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>和其他 <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> 則通知"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"向左移"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"向右移"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"全部關閉"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"打開<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收埋<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 90140cb..6d7652a 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"快速設定"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"工作列"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"已顯示工作列"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"已隱藏工作列"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"導覽列"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"一律顯示工作列"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"變更操作模式"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢位"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"「<xliff:g id="APP_NAME">%2$s</xliff:g>」的「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」通知"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g>和另外 <xliff:g id="BUBBLE_COUNT">%2$d</xliff:g> 則通知"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"向左移"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"向右移"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"全部關閉"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"展開「<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>」"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"收合「<xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>」"</string>
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 73be445..973ce80 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -130,7 +130,13 @@
<string name="taskbar_button_quick_settings" msgid="227662894293189391">"Amasethingi Asheshayo"</string>
<string name="taskbar_a11y_title" msgid="6432169809852243110">"I-Taskbar"</string>
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Ibha yomsebenzi ibonisiwe"</string>
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_left_title (4242431789851790046) -->
+ <skip />
+ <!-- no translation found for taskbar_a11y_shown_with_bubbles_right_title (8219065376188180113) -->
+ <skip />
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Ibha yomsebenzi ifihliwe"</string>
+ <!-- no translation found for taskbar_a11y_hidden_with_bubbles_title (7397395993149508087) -->
+ <skip />
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Ibha yokufuna"</string>
<string name="always_show_taskbar" msgid="3608801276107751229">"Bonisa i-Taskbar njalo."</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Shintsha imodi yokufuna"</string>
@@ -144,4 +150,9 @@
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ukugcwala kakhulu"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_bar_description_multiple_bubbles" msgid="3922207715357143648">"<xliff:g id="BUBBLE_BAR_BUBBLE_DESCRIPTION">%1$s</xliff:g> nokunye okungu-<xliff:g id="BUBBLE_COUNT">%2$d</xliff:g>"</string>
+ <string name="bubble_bar_action_move_left" msgid="3306922475737714758">"Iya kwesokunxele"</string>
+ <string name="bubble_bar_action_move_right" msgid="3455099638571411251">"Iya kwesokudla"</string>
+ <string name="bubble_bar_action_dismiss_all" msgid="3290722022983403060">"Chitha konke"</string>
+ <string name="bubble_bar_accessibility_announce_expand" msgid="1503192695527477102">"nweba <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
+ <string name="bubble_bar_accessibility_announce_collapse" msgid="928284600086798791">"goqa <xliff:g id="BUBBLE_DESCRIPTION">%1$s</xliff:g>"</string>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 98a2783..8bcbb33 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -299,10 +299,16 @@
<string name="taskbar_button_quick_settings">Quick Settings</string>
<!-- Accessibility title for the Taskbar window. [CHAR_LIMIT=NONE] -->
<string name="taskbar_a11y_title">Taskbar</string>
- <!-- Accessibility title for the Taskbar window appeared. [CHAR_LIMIT=NONE] -->
+ <!-- Accessibility title for the Taskbar window appeared. [CHAR_LIMIT=30] -->
<string name="taskbar_a11y_shown_title">Taskbar shown</string>
- <!-- Accessibility title for the Taskbar window being close. [CHAR_LIMIT=NONE] -->
+ <!-- Accessibility title for the Taskbar window appearing together with bubble bar on left. [CHAR_LIMIT=30] -->
+ <string name="taskbar_a11y_shown_with_bubbles_left_title">Taskbar & bubbles left shown</string>
+ <!-- Accessibility title for the Taskbar window appearing together with bubble bar on right. [CHAR_LIMIT=30] -->
+ <string name="taskbar_a11y_shown_with_bubbles_right_title">Taskbar & bubbles right shown</string>
+ <!-- Accessibility title for the Taskbar window being closed. [CHAR_LIMIT=30] -->
<string name="taskbar_a11y_hidden_title">Taskbar hidden</string>
+ <!-- Accessibility title for the Taskbar window being closed together with bubble bar. [CHAR_LIMIT=30] -->
+ <string name="taskbar_a11y_hidden_with_bubbles_title">Taskbar & bubbles hidden</string>
<!-- Accessibility title for the Taskbar window on phones. [CHAR_LIMIT=NONE] -->
<string name="taskbar_phone_a11y_title">Navigation bar</string>
<!-- Text in popup dialog for user to switch between always showing Taskbar or not. [CHAR LIMIT=30] -->
@@ -348,4 +354,8 @@
<string name="bubble_bar_action_move_right">Move right</string>
<!-- Action in accessibility menu to dismiss all bubbles. [CHAR_LIMIT=30] -->
<string name="bubble_bar_action_dismiss_all">Dismiss all</string>
+ <!-- Accessibility announcement when the bubble bar expands. [CHAR LIMIT=NONE]-->
+ <string name="bubble_bar_accessibility_announce_expand">expand <xliff:g id="bubble_description" example="some title from Messages">%1$s</xliff:g></string>
+ <!-- Accessibility announcement when the bubble bar collapses. [CHAR LIMIT=NONE]-->
+ <string name="bubble_bar_accessibility_announce_collapse">collapse <xliff:g id="bubble_description" example="some title from Messages">%1$s</xliff:g></string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 44601b7..fe1b403 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1252,15 +1252,25 @@
TransitionFilter homeCheck = new TransitionFilter();
// No need to handle the transition that also dismisses keyguard.
homeCheck.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+
homeCheck.mRequirements =
new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+ new TransitionFilter.Requirement(),
new TransitionFilter.Requirement()};
+
homeCheck.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
homeCheck.mRequirements[0].mTopActivity = mLauncher.getComponentName();
homeCheck.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
homeCheck.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+
homeCheck.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+
+ homeCheck.mRequirements[2].mNot = true;
+ homeCheck.mRequirements[2].mCustomAnimation = true;
+ homeCheck.mRequirements[2].mMustBeTask = true;
+ homeCheck.mRequirements[2].mMustBeIndependent = true;
+
SystemUiProxy.INSTANCE.get(mLauncher)
.registerRemoteTransition(mLauncherOpenTransition, homeCheck);
if (mBackAnimationController != null) {
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 0b18633..e925af6 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -28,7 +28,6 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.ClipData;
import android.content.ClipDescription;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -48,11 +47,11 @@
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.ArrayList;
import java.util.HashSet;
@@ -110,8 +109,8 @@
private WidgetsModel mModel;
private LauncherAppState mApp;
private WidgetPredictionsRequester mWidgetPredictionsRequester;
- private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {
- });
+ private final WidgetPickerDataProvider mWidgetPickerDataProvider =
+ new WidgetPickerDataProvider();
private int mDesiredWidgetWidth;
private int mDesiredWidgetHeight;
@@ -215,8 +214,8 @@
@NonNull
@Override
- public PopupDataProvider getPopupDataProvider() {
- return mPopupDataProvider;
+ public WidgetPickerDataProvider getWidgetPickerDataProvider() {
+ return mWidgetPickerDataProvider;
}
@Override
@@ -293,8 +292,6 @@
private void refreshAndBindWidgets() {
MODEL_EXECUTOR.execute(() -> {
LauncherAppState app = LauncherAppState.getInstance(this);
- Context context = app.getContext();
-
mModel.update(app, null);
bindWidgets(mModel.getWidgetsByPackageItem());
// Open sheet once widgets are available, so that it doesn't interrupt the open
@@ -317,7 +314,8 @@
shouldShowDefaultWidgets() ? builder.build(widgets,
mDefaultWidgetsFilter) : List.of();
- MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(allWidgets, defaultWidgets));
+ MAIN_EXECUTOR.execute(
+ () -> mWidgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets));
}
private void openWidgetsSheet() {
@@ -332,7 +330,7 @@
private void bindRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
// Bind recommendations once picker has finished open animation.
MAIN_EXECUTOR.getHandler().postDelayed(
- () -> mPopupDataProvider.setRecommendedWidgets(recommendedWidgets),
+ () -> mWidgetPickerDataProvider.setWidgetRecommendations(recommendedWidgets),
mDeviceProfile.bottomSheetOpenDuration);
}
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index a16031d..92d9516 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -290,6 +290,9 @@
writer.println(prefix + "\tmPredictionsEnabled: " + mPredictionsEnabled);
writer.println(prefix + "\tmPredictionUiUpdatePaused: " + mPredictionUiUpdatePaused);
writer.println(prefix + "\tmNumPredictedAppsPerRow: " + mNumPredictedAppsPerRow);
- writer.println(prefix + "\tmPredictedApps: " + mPredictedApps);
+ writer.println(prefix + "\tmPredictedApps: " + mPredictedApps.size());
+ for (WorkspaceItemInfo info : mPredictedApps) {
+ writer.println(prefix + "\t\t" + info);
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index de974ec..c50e82d 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -536,6 +536,9 @@
writer.println(prefix + "HotseatPredictionController");
writer.println(prefix + "\tFlags: " + getStateString(mPauseFlags));
writer.println(prefix + "\tmHotSeatItemsCount: " + mHotSeatItemsCount);
- writer.println(prefix + "\tmPredictedItems: " + mPredictedItems);
+ writer.println(prefix + "\tmPredictedItems: " + mPredictedItems.size());
+ for (ItemInfo info : mPredictedItems) {
+ writer.println(prefix + "\t\t" + info);
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 62cc0bb..6c7fe5b 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -19,10 +19,9 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.os.Debug;
-import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
@@ -145,7 +144,7 @@
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
}
- if (!enableDesktopWindowingWallpaperActivity() && wasVisible != isVisible) {
+ if (!WALLPAPER_ACTIVITY.isEnabled(mLauncher) && wasVisible != isVisible) {
// TODO: b/333533253 - Remove after flag rollout
if (mVisibleDesktopTasksCount > 0) {
setLauncherViewsVisibility(View.INVISIBLE);
@@ -189,7 +188,7 @@
notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
}
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
// TODO: b/333533253 - Clean up after flag rollout
@@ -289,7 +288,7 @@
* TODO: b/333533253 - Remove after flag rollout
*/
private void setLauncherViewsVisibility(int visibility) {
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
if (DEBUG) {
@@ -314,7 +313,7 @@
* TODO: b/333533253 - Remove after flag rollout
*/
private void markLauncherPaused() {
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
if (DEBUG) {
@@ -331,7 +330,7 @@
* TODO: b/333533253 - Remove after flag rollout
*/
private void markLauncherResumed() {
- if (enableDesktopWindowingWallpaperActivity()) {
+ if (WALLPAPER_ACTIVITY.isEnabled(mLauncher)) {
return;
}
if (DEBUG) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 0add1c4..3981e43 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -21,10 +21,11 @@
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IGNORE_IN_APP;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.graphics.Rect;
import android.window.RemoteTransition;
import androidx.annotation.NonNull;
@@ -181,6 +182,24 @@
}
/**
+ * Returns the bounds of launcher's hotseat.
+ */
+ public void getHotseatBounds(Rect hotseatBoundsOut) {
+ DeviceProfile launcherDP = mLauncher.getDeviceProfile();
+ if (launcherDP.isQsbInline) {
+ // Not currently supported.
+ hotseatBoundsOut.setEmpty();
+ return;
+ }
+ int left = (launcherDP.widthPx - launcherDP.getHotseatWidthPx()
+ - mLauncher.getHotseat().getUnusedHorizontalSpace()) / 2;
+ int right = left + launcherDP.getHotseatWidthPx();
+ int bottom = launcherDP.getHotseatLayoutPadding(mLauncher).bottom;
+ int top = bottom - launcherDP.hotseatCellHeightPx;
+ hotseatBoundsOut.set(left, top, right, bottom);
+ }
+
+ /**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
@Override
@@ -214,7 +233,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (!enableDesktopWindowingWallpaperActivity()
+ if (!WALLPAPER_ACTIVITY.isEnabled(mLauncher)
&& desktopController != null
&& desktopController.areDesktopTasksVisible()) {
// TODO: b/333533253 - Remove after flag rollout
@@ -272,6 +291,10 @@
* app launch animation.
*/
public void setIgnoreInAppFlagForSync(boolean enabled) {
+ if (mControllers == null) {
+ // This method can be called before init() is called.
+ return;
+ }
mControllers.taskbarStashController.updateStateForFlag(FLAG_IGNORE_IN_APP, enabled);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 252f2a8..266d0b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -207,8 +207,11 @@
* Creates and returns a {@link RevealOutlineAnimation} Animator that updates the stashed handle
* shape and size. When stashed, the shape is a thin rounded pill. When unstashed, the shape
* morphs into the size of where the taskbar icons will be.
+ *
+ * @param taskbarToHotseatOffsets A Rect of offsets used to transform the bounds of the
+ * stashed handle to wrap around the hotseat items.
*/
- public Animator createRevealAnimToIsStashed(boolean isStashed) {
+ public Animator createRevealAnimToIsStashed(boolean isStashed, Rect taskbarToHotseatOffsets) {
Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds());
float startRadius = mStashedHandleRadius;
@@ -219,6 +222,13 @@
visualBounds.bottom += heightDiff;
startRadius = visualBounds.height() / 2f;
+
+ // We use these offsets to create a larger stashed handle to wrap around the items
+ // of the hotseat. This is only used for certain animations.
+ visualBounds.top += taskbarToHotseatOffsets.top;
+ visualBounds.bottom += taskbarToHotseatOffsets.bottom;
+ visualBounds.left += taskbarToHotseatOffsets.left;
+ visualBounds.right += taskbarToHotseatOffsets.right;
}
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 3048243..e4f7262 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.Flags.enableTaskbarCustomization;
import static com.android.launcher3.Utilities.calculateTextHeight;
import static com.android.launcher3.Utilities.isRunningInTestHarness;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
@@ -110,8 +111,12 @@
import com.android.launcher3.taskbar.bubbles.BubbleDismissController;
import com.android.launcher3.taskbar.bubbles.BubbleDragController;
import com.android.launcher3.taskbar.bubbles.BubblePinController;
-import com.android.launcher3.taskbar.bubbles.BubbleStashController;
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider;
+import com.android.launcher3.taskbar.bubbles.stashing.DeviceProfileDimensionsProviderAdapter;
+import com.android.launcher3.taskbar.bubbles.stashing.PersistentBubbleStashController;
+import com.android.launcher3.taskbar.bubbles.stashing.TransientBubbleStashController;
import com.android.launcher3.taskbar.customization.TaskbarFeatureEvaluator;
import com.android.launcher3.taskbar.customization.TaskbarSpecsEvaluator;
import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
@@ -140,6 +145,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -149,6 +155,7 @@
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
+import com.android.wm.shell.Flags;
import java.io.PrintWriter;
import java.util.Collections;
@@ -207,9 +214,9 @@
private final LauncherPrefs mLauncherPrefs;
- private final TaskbarFeatureEvaluator mTaskbarFeatureEvaluator;
+ private TaskbarFeatureEvaluator mTaskbarFeatureEvaluator;
- private final TaskbarSpecsEvaluator mTaskbarSpecsEvaluator;
+ private TaskbarSpecsEvaluator mTaskbarSpecsEvaluator;
public TaskbarActivityContext(Context windowContext,
@Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
@@ -221,12 +228,14 @@
applyDeviceProfile(launcherDp);
final Resources resources = getResources();
- mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this);
- mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator(
- this,
- mTaskbarFeatureEvaluator,
- mDeviceProfile.inv.numRows,
- mDeviceProfile.inv.numColumns);
+ if (enableTaskbarCustomization()) {
+ mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this);
+ mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator(
+ this,
+ mTaskbarFeatureEvaluator,
+ mDeviceProfile.inv.numRows,
+ mDeviceProfile.inv.numColumns);
+ }
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
@@ -247,15 +256,18 @@
mWindowManager = c.getSystemService(WindowManager.class);
// Inflate views.
- int taskbarLayout = DisplayController.isTransientTaskbar(this) && !isPhoneMode()
- ? R.layout.transient_taskbar
- : R.layout.taskbar;
+ final boolean isTransientTaskbar = DisplayController.isTransientTaskbar(this)
+ && !isPhoneMode();
+ int taskbarLayout = isTransientTaskbar ? R.layout.transient_taskbar : R.layout.taskbar;
mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(taskbarLayout, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
NearestTouchFrame navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
- BubbleBarView bubbleBarView = mDragLayer.findViewById(R.id.taskbar_bubbles);
+ BubbleBarView bubbleBarView = null;
+ if (isTransientTaskbar || Flags.enableBubbleBarInPersistentTaskBar()) {
+ bubbleBarView = mDragLayer.findViewById(R.id.taskbar_bubbles);
+ }
StashedHandleView bubbleHandleView = mDragLayer.findViewById(R.id.stashed_bubble_handle);
mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
@@ -264,11 +276,21 @@
Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
BubbleBarController.onTaskbarRecreated();
if (BubbleBarController.isBubbleBarEnabled() && bubbleBarView != null) {
+ Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
+ if (isTransientTaskbar) {
+ bubbleHandleController = Optional.of(
+ new BubbleStashedHandleViewController(this, bubbleHandleView));
+ }
+ TaskbarHotseatDimensionsProvider dimensionsProvider =
+ new DeviceProfileDimensionsProviderAdapter(this);
+ BubbleStashController bubbleStashController = isTransientTaskbar
+ ? new TransientBubbleStashController(dimensionsProvider, getResources())
+ : new PersistentBubbleStashController(dimensionsProvider);
bubbleControllersOptional = Optional.of(new BubbleControllers(
new BubbleBarController(this, bubbleBarView),
new BubbleBarViewController(this, bubbleBarView),
- new BubbleStashController(this),
- new BubbleStashedHandleViewController(this, bubbleHandleView),
+ bubbleStashController,
+ bubbleHandleController,
new BubbleDragController(this),
new BubbleDismissController(this, mDragLayer),
new BubbleBarPinController(this, mDragLayer,
@@ -315,6 +337,7 @@
new TaskbarTranslationController(this),
new TaskbarSpringOnStashController(this),
new TaskbarRecentAppsController(
+ this,
RecentsModel.INSTANCE.get(this),
LauncherActivityInterface.INSTANCE::getDesktopVisibilityController),
TaskbarEduTooltipController.newInstance(this),
@@ -339,6 +362,38 @@
}
/**
+ * Calculate the offsets needed to transform the transient taskbar bounds to the hotseat bounds.
+ * @return The offsets will be stored in a Rect
+ */
+ public Rect calculateTaskbarToHotseatOffsets(Rect hotseatBounds) {
+ Rect taskbar = getTransientTaskbarBounds();
+ Rect offsets = new Rect();
+
+ offsets.left = hotseatBounds.left - taskbar.left;
+ offsets.right = hotseatBounds.right - taskbar.right;
+
+ int heightDiff = hotseatBounds.height() - taskbar.height();
+ offsets.top = (taskbar.height() - heightDiff) / 2;
+
+ int gleanedTaskbarPadding = (mDeviceProfile.taskbarHeight
+ - getTransientTaskbarBounds().height()) / 2;
+ offsets.left -= gleanedTaskbarPadding;
+ offsets.top -= gleanedTaskbarPadding;
+ offsets.right += gleanedTaskbarPadding;
+
+ // Bottom is relative to the bottom of layout, so we can calculate it with padding included.
+ offsets.bottom = (hotseatBounds.height() - taskbar.height()) / 2;
+
+ // Update bounds in taskbar background
+ if (hotseatBounds.isEmpty()) {
+ mDragLayer.getTaskbarToHotseatOffsetRect().setEmpty();
+ } else {
+ mDragLayer.getTaskbarToHotseatOffsetRect().set(offsets);
+ }
+ return offsets;
+ }
+
+ /**
* Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update
* the icon size
*/
@@ -813,7 +868,7 @@
*/
public void setUIController(@NonNull TaskbarUIController uiController) {
mControllers.setUiController(uiController);
- if (mControllers.bubbleControllers.isEmpty()) {
+ if (BubbleBarController.isBubbleBarEnabled() && mControllers.bubbleControllers.isEmpty()) {
// if the bubble bar was visible in a previous configuration of taskbar and is being
// recreated now without bubbles, clean up any bubble bar adjustments from hotseat
bubbleBarVisibilityChanged(/* isVisible= */ false);
@@ -889,8 +944,9 @@
mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags);
mControllers.bubbleControllers.ifPresent(controllers -> {
controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags);
- controllers.bubbleStashedHandleViewController.setIsHomeButtonDisabled(
- mControllers.navbarButtonsViewController.isHomeDisabled());
+ controllers.bubbleStashedHandleViewController.ifPresent(controller ->
+ controller.setIsHomeButtonDisabled(
+ mControllers.navbarButtonsViewController.isHomeDisabled()));
});
}
@@ -928,6 +984,7 @@
public void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
mControllers.navbarButtonsViewController.onTransitionModeUpdated(barMode, checkBarModes);
}
+
public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
.updateValue(darkIntensity);
@@ -1356,7 +1413,8 @@
if (foundTask != null) {
TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
if (foundTaskView != null
- && foundTaskView.isVisibleToUser()) {
+ && foundTaskView.isVisibleToUser()
+ && !(foundTaskView instanceof DesktopTaskView)) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
foundTaskView.launchTasks();
@@ -1407,7 +1465,6 @@
return;
}
}
-
startActivity(intent);
} else {
getSystemService(LauncherApps.class).startMainActivity(
@@ -1615,6 +1672,18 @@
}
/**
+ * Returns the bounds of launcher's hotseat (if exists).
+ */
+ public void getHotseatBounds(Rect hotseatBoundsOut) {
+ TaskbarUIController uiController = mControllers.uiController;
+ if (uiController instanceof LauncherTaskbarUIController launcherController) {
+ launcherController.getHotseatBounds(hotseatBoundsOut);
+ } else {
+ hotseatBoundsOut.setEmpty();
+ }
+ }
+
+ /**
* Called when we determine the touchable region.
*
* @param exclude {@code true} then the magnification region computation will omit the window.
@@ -1662,10 +1731,12 @@
return mControllers.taskbarStashController.isInStashedLauncherState();
}
+ @Nullable
public TaskbarFeatureEvaluator getTaskbarFeatureEvaluator() {
return mTaskbarFeatureEvaluator;
}
+ @Nullable
public TaskbarSpecsEvaluator getTaskbarSpecsEvaluator() {
return mTaskbarSpecsEvaluator;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 2737cbd..ee64060 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -21,6 +21,7 @@
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
+import android.graphics.Rect
import android.graphics.RectF
import com.android.app.animation.Interpolators
import com.android.launcher3.R
@@ -58,6 +59,9 @@
var translationYForSwipe = 0f
var translationYForStash = 0f
+ // When not empty, we can use this to transform transient taskbar background to hotseat bounds.
+ val taskbarToHotseatOffsetRect = Rect()
+
private val transientBackgroundBounds = context.transientTaskbarBounds
private val shadowAlpha: Float
@@ -218,6 +222,12 @@
val radius = newBackgroundHeight / 2f
val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
+ // Used to transform the background so that it wraps around the items on the hotseat.
+ val hotseatOffsetLeft = taskbarToHotseatOffsetRect.left * progress
+ val hotseatOffsetTop = taskbarToHotseatOffsetRect.top * progress
+ val hotseatOffsetRight = taskbarToHotseatOffsetRect.right * progress
+ val hotseatOffsetBottom = taskbarToHotseatOffsetRect.bottom * progress
+
// Aligns the bottom with the bottom of the stashed handle.
val bottom =
canvas.height - bottomMargin +
@@ -242,10 +252,10 @@
strokePaint.alpha = (paint.alpha * strokeAlpha) / 255
lastDrawnTransientRect.set(
- transientBackgroundBounds.left + halfWidthDelta,
- bottom - newBackgroundHeight,
- transientBackgroundBounds.right - halfWidthDelta,
- bottom
+ transientBackgroundBounds.left + halfWidthDelta + hotseatOffsetLeft,
+ bottom - newBackgroundHeight + hotseatOffsetTop,
+ transientBackgroundBounds.right - halfWidthDelta + hotseatOffsetRight,
+ bottom + hotseatOffsetBottom
)
val horizontalInset = fullWidth * widthInsetPercentage
lastDrawnTransientRect.inset(horizontalInset, 0f)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 0645972..d94d9175 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -165,6 +165,7 @@
taskbarOverlayController.init(this);
taskbarAllAppsController.init(this, sharedState.allAppsVisible);
navButtonController.init(this);
+ bubbleControllers.ifPresent(controllers -> controllers.init(this));
taskbarInsetsController.init(this);
voiceInteractionWindowController.init(this);
taskbarRecentAppsController.init(this);
@@ -172,7 +173,6 @@
taskbarEduTooltipController.init(this);
keyboardQuickSwitchController.init(this);
taskbarPinningController.init(this, mSharedState);
- bubbleControllers.ifPresent(controllers -> controllers.init(this));
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index a9b34d2..a090956 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.media.permission.SafeCloseable;
import android.util.AttributeSet;
@@ -259,6 +260,11 @@
return mBackgroundRenderer.getLastDrawnTransientRect();
}
+ /** Returns the rect used to transform transient taskbar to the hotseat */
+ public Rect getTaskbarToHotseatOffsetRect() {
+ return mBackgroundRenderer.getTaskbarToHotseatOffsetRect();
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index d57c483..06376d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -33,6 +33,7 @@
import android.widget.TextView
import androidx.annotation.IntDef
import androidx.annotation.LayoutRes
+import androidx.annotation.VisibleForTesting
import androidx.core.text.HtmlCompat
import androidx.core.view.updateLayoutParams
import com.airbnb.lottie.LottieAnimationView
@@ -87,7 +88,7 @@
!activityContext.isTinyTaskbar
}
- private val isOpen: Boolean
+ val isTooltipOpen: Boolean
get() = tooltip?.isOpen ?: false
val isBeforeTooltipFeaturesStep: Boolean
@@ -96,7 +97,8 @@
private lateinit var controllers: TaskbarControllers
// Keep track of whether the user has seen the Search Edu
- private var userHasSeenSearchEdu: Boolean
+ @VisibleForTesting
+ var userHasSeenSearchEdu: Boolean
get() {
return TASKBAR_SEARCH_EDU_SEEN.get(activityContext)
}
@@ -409,7 +411,7 @@
override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
pw?.println(prefix + "TaskbarEduTooltipController:")
pw?.println("$prefix\tisTooltipEnabled=$isTooltipEnabled")
- pw?.println("$prefix\tisOpen=$isOpen")
+ pw?.println("$prefix\tisOpen=$isTooltipOpen")
pw?.println("$prefix\ttooltipStep=$tooltipStep")
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 2103ebb..9dee473 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -64,6 +64,10 @@
companion object {
private const val INDEX_LEFT = 0
private const val INDEX_RIGHT = 1
+
+ private fun Region.addBoundsToRegion(bounds: Rect?) {
+ bounds?.let { op(it, Region.Op.UNION) }
+ }
}
/** The bottom insets taskbar provides to the IME when IME is visible. */
@@ -128,46 +132,25 @@
}
}
+ val bubbleControllers = controllers.bubbleControllers.getOrNull()
val taskbarTouchableHeight = controllers.taskbarStashController.touchableHeight
val bubblesTouchableHeight =
- if (controllers.bubbleControllers.isPresent) {
- controllers.bubbleControllers.get().bubbleStashController.touchableHeight
- } else {
- 0
- }
+ bubbleControllers?.bubbleStashController?.getTouchableHeight() ?: 0
+ // add bounds for task bar and bubble bar stash controllers
val touchableHeight = max(taskbarTouchableHeight, bubblesTouchableHeight)
-
- if (
- controllers.bubbleControllers.isPresent &&
- controllers.bubbleControllers.get().bubbleStashController.isBubblesShowingOnHome
- ) {
- val iconBounds =
- controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds
- defaultTouchableRegion.set(
- iconBounds.left,
- iconBounds.top,
- iconBounds.right,
- iconBounds.bottom
- )
- } else {
- defaultTouchableRegion.set(
- 0,
- windowLayoutParams.height - touchableHeight,
- context.deviceProfile.widthPx,
- windowLayoutParams.height
- )
-
- // if there's an animating bubble add it to the touch region so that it's clickable
- val isAnimatingNewBubble =
- controllers.bubbleControllers
- .getOrNull()
- ?.bubbleBarViewController
- ?.isAnimatingNewBubble
- ?: false
- if (isAnimatingNewBubble) {
- val iconBounds =
- controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds
- defaultTouchableRegion.op(iconBounds, Region.Op.UNION)
+ defaultTouchableRegion.set(
+ 0,
+ windowLayoutParams.height - touchableHeight,
+ context.deviceProfile.widthPx,
+ windowLayoutParams.height
+ )
+ if (bubbleControllers != null) {
+ val bubbleBarViewController = bubbleControllers.bubbleBarViewController
+ val isBubbleBarVisible = bubbleControllers.bubbleStashController.isBubbleBarVisible()
+ val isAnimatingNewBubble = bubbleBarViewController.isAnimatingNewBubble
+ // if bubble bar is visible or animating new bubble, add bar bounds to the touch region
+ if (isBubbleBarVisible || isAnimatingNewBubble) {
+ defaultTouchableRegion.addBoundsToRegion(bubbleBarViewController.bubbleBarBounds)
}
}
@@ -358,13 +341,6 @@
*/
fun updateInsetsTouchability(insetsInfo: ViewTreeObserver.InternalInsetsInfo) {
insetsInfo.touchableRegion.setEmpty()
- // Always have nav buttons be touchable
- controllers.navbarButtonsViewController.addVisibleButtonsRegion(
- context.dragLayer,
- insetsInfo.touchableRegion
- )
- debugTouchableRegion.lastSetTouchableBounds.set(insetsInfo.touchableRegion.bounds)
-
val bubbleBarVisible =
controllers.bubbleControllers.isPresent &&
controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible()
@@ -426,7 +402,7 @@
// Include the bounds of the bubble bar in the touchable region if they exist.
if (bubbleBarBounds != null) {
- region.op(bubbleBarBounds, Region.Op.UNION)
+ region.addBoundsToRegion(bubbleBarBounds)
}
insetsInfo.touchableRegion.set(region)
debugTouchableRegion.lastSetTouchableReason = "Transient Taskbar is in Overview"
@@ -443,6 +419,12 @@
debugTouchableRegion.lastSetTouchableReason =
"Icons are not visible, but other components such as 3 buttons might be"
}
+ // Always have nav buttons be touchable
+ controllers.navbarButtonsViewController.addVisibleButtonsRegion(
+ context.dragLayer,
+ insetsInfo.touchableRegion
+ )
+ debugTouchableRegion.lastSetTouchableBounds.set(insetsInfo.touchableRegion.bounds)
context.excludeFromMagnificationRegion(insetsIsTouchableRegion)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index cb9f24a..20ab32e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -250,7 +250,7 @@
applyState(0);
- mCanSyncViews = true;
+ mCanSyncViews = !mControllers.taskbarActivityContext.isPhoneMode();
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
updateOverviewDragState(mLauncherState);
}
@@ -263,7 +263,7 @@
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
- mCanSyncViews = true;
+ mCanSyncViews = !mControllers.taskbarActivityContext.isPhoneMode();
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
}
@@ -743,8 +743,7 @@
if (firstFrameVisChanged && mCanSyncViews && !Utilities.isRunningInTestHarness()) {
ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(),
mControllers.taskbarActivityContext.getDragLayer(),
- () -> {
- });
+ () -> {});
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 5c08116..1a168a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar
+import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
@@ -26,8 +27,8 @@
import com.android.quickstep.RecentsModel
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
-import com.android.window.flags.Flags.enableDesktopWindowingMode
-import com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import java.io.PrintWriter
/**
@@ -36,18 +37,22 @@
* - When in Desktop Mode: show the currently running (open) Tasks
*/
class TaskbarRecentAppsController(
+ context: Context,
private val recentsModel: RecentsModel,
// Pass a provider here instead of the actual DesktopVisibilityController instance since that
// instance might not be available when this constructor is called.
private val desktopVisibilityControllerProvider: () -> DesktopVisibilityController?,
) : LoggableTaskbarController {
- // TODO(b/335401172): unify DesktopMode checks in Launcher.
var canShowRunningApps =
- enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()
+ DesktopModeStatus.canEnterDesktopMode(context) &&
+ DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(context)
@VisibleForTesting
set(isEnabledFromTest) {
field = isEnabledFromTest
+ if (!field && !canShowRecentApps) {
+ recentsModel.unregisterRecentTasksChangedListener()
+ }
}
// TODO(b/343532825): Add a setting to disable Recents even when the flag is on.
@@ -55,6 +60,9 @@
@VisibleForTesting
set(isEnabledFromTest) {
field = isEnabledFromTest
+ if (!field && !canShowRunningApps) {
+ recentsModel.unregisterRecentTasksChangedListener()
+ }
}
// Initialized in init.
@@ -114,8 +122,10 @@
fun init(taskbarControllers: TaskbarControllers) {
controllers = taskbarControllers
- recentsModel.registerRecentTasksChangedListener(recentTasksChangedListener)
- reloadRecentTasksIfNeeded()
+ if (canShowRunningApps || canShowRecentApps) {
+ recentsModel.registerRecentTasksChangedListener(recentTasksChangedListener)
+ reloadRecentTasksIfNeeded()
+ }
}
fun onDestroy() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 48d2bc2..5e7c7ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -20,8 +20,8 @@
import static com.android.launcher3.taskbar.bubbles.BubbleBarController.isBubbleBarEnabled;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
-import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
+import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import android.animation.ObjectAnimator;
import android.view.animation.Interpolator;
@@ -65,6 +65,7 @@
*/
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ onTaskbarVisibilityChanged(mControllers.taskbarViewController.getTaskbarVisibility());
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 0c5ad42..5d6fdc1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -42,6 +42,7 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.RemoteAction;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.os.SystemClock;
import android.util.Log;
@@ -202,6 +203,7 @@
* by not scaling the height of the taskbar background.
*/
private static final int TRANSITION_UNSTASH_SUW_MANUAL = 3;
+ private static final Rect EMPTY_RECT = new Rect();
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -713,7 +715,7 @@
}
fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
- .createRevealAnimToIsStashed(isStashed));
+ .createRevealAnimToIsStashed(isStashed, EMPTY_RECT));
// Return the stashed handle to its default scale in case it was changed as part of the
// feedforward hint. Note that the reveal animation above also visually scales it.
fullLengthAnimatorSet.play(mTaskbarStashedHandleHintScale.animateToValue(1f));
@@ -763,6 +765,19 @@
}
}
+
+ Rect taskbarToHotseatOffsets = new Rect();
+ if (enableScalingRevealHomeAnimation() && animationType == TRANSITION_HOME_TO_APP) {
+ Rect hotseatRect = new Rect();
+ mActivity.getHotseatBounds(hotseatRect);
+
+ // Calculate and store offsets so that we can sync with the taskbar stashed handle
+ taskbarToHotseatOffsets.set(
+ mActivity.calculateTaskbarToHotseatOffsets(hotseatRect));
+ as.addListener(AnimatorListeners.forEndCallback(
+ () -> mActivity.calculateTaskbarToHotseatOffsets(EMPTY_RECT)));
+ }
+
play(as, mTaskbarStashedHandleAlpha.animateToValue(stashedHandleAlphaTarget),
backgroundAndHandleAlphaStartDelay,
backgroundAndHandleAlphaDuration, LINEAR);
@@ -808,10 +823,12 @@
}
mControllers.taskbarViewController.addRevealAnimToIsStashed(skippable, isStashed, duration,
- EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL);
+ EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL,
+ animationType == TRANSITION_HOME_TO_APP);
play(skippable, mControllers.stashedHandleViewController
- .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED);
+ .createRevealAnimToIsStashed(isStashed, taskbarToHotseatOffsets), 0, duration,
+ EMPHASIZED);
// Return the stashed handle to its default scale in case it was changed as part of the
// feedforward hint. Note that the reveal animation above also visually scales it.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 144c0c2..5a5d6d0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -95,7 +95,8 @@
mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
mControllers.bubbleControllers.ifPresent(controllers -> {
controllers.bubbleBarViewController.setTranslationYForSwipe(transY);
- controllers.bubbleStashedHandleViewController.setTranslationYForSwipe(transY);
+ controllers.bubbleStashedHandleViewController.ifPresent(
+ controller -> controller.setTranslationYForSwipe(transY));
});
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index a2278ec..2ada5ba 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -174,11 +174,11 @@
|| mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
}
- /** Checks if the given {@link MotionEvent} is over the bubble bar stash handle. */
- public boolean isEventOverBubbleBarStashHandle(MotionEvent ev) {
+ /** Checks if the given {@link MotionEvent} is over the bubble bar views. */
+ public boolean isEventOverBubbleBarViews(MotionEvent ev) {
return mControllers.bubbleControllers.map(
bubbleControllers ->
- bubbleControllers.bubbleStashController.isEventOverStashHandle(ev))
+ bubbleControllers.bubbleStashController.isEventOverBubbleBarViews(ev))
.orElse(false);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c42d6c6..e58069a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -72,6 +72,7 @@
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import java.util.List;
import java.util.function.Predicate;
@@ -246,12 +247,34 @@
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
- announceForAccessibility(mContext.getString(R.string.taskbar_a11y_shown_title));
+ announceTaskbarShown();
} else if (action == AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) {
- announceForAccessibility(mContext.getString(R.string.taskbar_a11y_hidden_title));
+ announceTaskbarHidden();
}
return super.performAccessibilityActionInternal(action, arguments);
+ }
+ private void announceTaskbarShown() {
+ BubbleBarLocation bubbleBarLocation = mControllerCallbacks.getBubbleBarLocationIfVisible();
+ if (bubbleBarLocation == null) {
+ announceForAccessibility(mContext.getString(R.string.taskbar_a11y_shown_title));
+ } else if (bubbleBarLocation.isOnLeft(isLayoutRtl())) {
+ announceForAccessibility(
+ mContext.getString(R.string.taskbar_a11y_shown_with_bubbles_left_title));
+ } else {
+ announceForAccessibility(
+ mContext.getString(R.string.taskbar_a11y_shown_with_bubbles_right_title));
+ }
+ }
+
+ private void announceTaskbarHidden() {
+ BubbleBarLocation bubbleBarLocation = mControllerCallbacks.getBubbleBarLocationIfVisible();
+ if (bubbleBarLocation == null) {
+ announceForAccessibility(mContext.getString(R.string.taskbar_a11y_hidden_title));
+ } else {
+ announceForAccessibility(
+ mContext.getString(R.string.taskbar_a11y_hidden_with_bubbles_title));
+ }
}
protected void announceAccessibilityChanges() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 3c646cb..e6cac2f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -23,8 +23,12 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.internal.jank.Cuj;
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
/**
* Callbacks for {@link TaskbarView} to interact with its controller.
@@ -104,4 +108,18 @@
mControllers.taskbarScrimViewController.onTaskbarVisibilityChanged(
mTaskbarView.getVisibility());
}
+
+ /**
+ * Get current location of bubble bar, if it is visible.
+ * Returns {@code null} if bubble bar is not shown.
+ */
+ @Nullable
+ public BubbleBarLocation getBubbleBarLocationIfVisible() {
+ BubbleBarViewController bubbleBarViewController =
+ mControllers.bubbleControllers.map(c -> c.bubbleBarViewController).orElse(null);
+ if (bubbleBarViewController != null && bubbleBarViewController.isBubbleBarVisible()) {
+ return bubbleBarViewController.getBubbleBarLocation();
+ }
+ return null;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index b21c414..ffa4819 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -33,6 +33,7 @@
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_PINNING_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
+import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -232,6 +233,13 @@
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
}
+ /**
+ * Gets the taskbar {@link View.Visibility visibility}.
+ */
+ public int getTaskbarVisibility() {
+ return mTaskbarView.getVisibility();
+ }
+
public boolean areIconsVisible() {
return mTaskbarView.areIconsVisible();
}
@@ -574,7 +582,8 @@
* @param interpolator The interpolator to use for all animations.
*/
public void addRevealAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
- Interpolator interpolator, boolean dispatchOnAnimationStart) {
+ Interpolator interpolator, boolean dispatchOnAnimationStart,
+ boolean isHomeToAppAnimation) {
AnimatorSet reveal = new AnimatorSet();
Rect stashedBounds = new Rect();
@@ -623,8 +632,21 @@
reveal.play(ObjectAnimator.ofFloat(mtd.getTranslationX(INDEX_TASKBAR_REVEAL_ANIM),
MULTI_PROPERTY_VALUE, transX)
.setDuration(duration));
- reveal.play(ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
- MULTI_PROPERTY_VALUE, transY));
+
+ if (enableScalingRevealHomeAnimation()) {
+ // Delay y-translation by 1 frame to keep icons within the bounds of the bg.
+ int delay = isHomeToAppAnimation ? getSingleFrameMs(mActivity) : 0;
+ ObjectAnimator yAnimator =
+ ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
+ MULTI_PROPERTY_VALUE, transY)
+ .setDuration(Math.max(0, duration - delay));
+ yAnimator.setStartDelay(delay);
+ reveal.play(yAnimator);
+ } else {
+ reveal.play(
+ ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM),
+ MULTI_PROPERTY_VALUE, transY));
+ }
as.addListener(forEndCallback(() ->
mtd.setTranslation(INDEX_TASKBAR_REVEAL_ANIM, 0, 0)));
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 5a5ff8e..619c9c4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -41,7 +41,8 @@
class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
- private val isSeparateBackgroundEnabled = !DisplayController.isTransientTaskbar(context)
+ private val isSeparateBackgroundEnabled =
+ !DisplayController.isTransientTaskbar(context) && !context.isPhoneMode
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
private val nonTouchableInsetsComputer =
ViewTreeObserver.OnComputeInternalInsetsListener {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubbleIconsFactory.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubbleIconsFactory.kt
new file mode 100644
index 0000000..4330c5b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubbleIconsFactory.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.bubbles
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import com.android.launcher3.icons.BaseIconFactory
+
+/** Bubble icons factory for the bubble bar. */
+class BubbleBarBubbleIconsFactory(context: Context, bubbleSize: Int) :
+ BaseIconFactory(context, context.resources.configuration.densityDpi, bubbleSize) {
+
+ /** Creates shadowed icon for the bubble bar. */
+ fun createShadowedIconBitmap(
+ icon: Drawable,
+ scale: Float,
+ ): Bitmap = super.createIconBitmap(icon, scale, MODE_WITH_SHADOW)
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 5be0171..58cd042 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -63,6 +63,7 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -76,6 +77,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -144,7 +146,7 @@
private ImeVisibilityChecker mImeVisibilityChecker;
private BubbleBarViewController mBubbleBarViewController;
private BubbleStashController mBubbleStashController;
- private BubbleStashedHandleViewController mBubbleStashedHandleViewController;
+ private Optional<BubbleStashedHandleViewController> mBubbleStashedHandleViewController;
private BubblePinController mBubblePinController;
// Cache last sent top coordinate to avoid sending duplicate updates to shell
@@ -221,8 +223,9 @@
bubbleControllers.runAfterInit(() -> {
mBubbleBarViewController.setHiddenForBubbles(
!sBubbleBarEnabled || mBubbles.isEmpty());
- mBubbleStashedHandleViewController.setHiddenForBubbles(
- !sBubbleBarEnabled || mBubbles.isEmpty());
+ mBubbleStashedHandleViewController.ifPresent(
+ controller -> controller.setHiddenForBubbles(
+ !sBubbleBarEnabled || mBubbles.isEmpty()));
mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
key -> setSelectedBubbleInternal(mBubbles.get(key)));
mBubbleBarViewController.setBoundsChangeListener(this::onBubbleBarBoundsChanged);
@@ -258,10 +261,11 @@
mBubbleBarViewController.setHiddenForSysui(hideBubbleBar);
boolean hideHandleView = (flags & MASK_HIDE_HANDLE_VIEW) != 0;
- mBubbleStashedHandleViewController.setHiddenForSysui(hideHandleView);
+ mBubbleStashedHandleViewController.ifPresent(
+ controller -> controller.setHiddenForSysui(hideHandleView));
boolean sysuiLocked = (flags & MASK_SYSUI_LOCKED) != 0;
- mBubbleStashController.onSysuiLockedStateChange(sysuiLocked);
+ mBubbleStashController.setSysuiLocked(sysuiLocked);
}
//
@@ -387,7 +391,8 @@
// Adds and removals have happened, update visibility before any other visual changes.
mBubbleBarViewController.setHiddenForBubbles(mBubbles.isEmpty());
- mBubbleStashedHandleViewController.setHiddenForBubbles(mBubbles.isEmpty());
+ mBubbleStashedHandleViewController.ifPresent(
+ controller -> controller.setHiddenForBubbles(mBubbles.isEmpty()));
if (mBubbles.isEmpty()) {
// all bubbles were removed. clear the selected bubble
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
index 9e5ffc9..a6b0860 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
@@ -27,6 +27,7 @@
import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.wm.shell.common.bubbles.BaseBubblePinController
import com.android.wm.shell.common.bubbles.BubbleBarLocation
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index fab7975..7d27a90 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -959,11 +959,14 @@
float fullElevationForChild = (MAX_BUBBLES * mBubbleElevation) - i;
bv.setZ(fullElevationForChild * elevationState);
- // only update the dot scale if we're expanding or collapsing
+ // only update the dot and badge scale if we're expanding or collapsing
if (mWidthAnimator.isRunning()) {
// The dot for the selected bubble scales in the opposite direction of the expansion
// animation.
bv.showDotIfNeeded(bv == mSelectedBubbleView ? 1 - widthState : widthState);
+ // The badge for the selected bubble is always at full scale. All other bubbles
+ // scale according to the expand animation.
+ bv.setBadgeScale(bv == mSelectedBubbleView ? 1 : widthState);
}
if (mIsBarExpanded) {
@@ -972,16 +975,12 @@
// where the bubble will end up when the animation ends
final float targetX = expandedX + expandedBarShift;
bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
- // When we're expanded, the badge is visible for all bubbles
- bv.updateBadgeVisibility(/* show= */ true);
bv.setAlpha(1);
} else {
// If bar is on the right, account for bubble bar expanding and shifting left
final float collapsedBarShift = onLeft ? 0 : currentWidth - collapsedWidth;
final float targetX = collapsedX + collapsedBarShift;
bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
- // The badge is always visible for the first bubble
- bv.updateBadgeVisibility(/* show= */ i == 0);
// If we're fully collapsed, hide all bubbles except for the first 2. If there are
// only 2 bubbles, hide the second bubble as well because it's the overflow.
if (widthState == 0) {
@@ -1228,6 +1227,7 @@
mWidthAnimator.reverse();
}
updateBubbleAccessibilityStates();
+ announceExpandedStateChange();
}
}
@@ -1344,6 +1344,26 @@
setContentDescription(contentDesc);
}
+ private void announceExpandedStateChange() {
+ final CharSequence selectedBubbleContentDesc;
+ if (mSelectedBubbleView != null) {
+ selectedBubbleContentDesc = mSelectedBubbleView.getContentDescription();
+ } else {
+ selectedBubbleContentDesc = getResources().getString(
+ R.string.bubble_bar_bubble_fallback_description);
+ }
+
+ final String msg;
+ if (mIsBarExpanded) {
+ msg = getResources().getString(R.string.bubble_bar_accessibility_announce_expand,
+ selectedBubbleContentDesc);
+ } else {
+ msg = getResources().getString(R.string.bubble_bar_accessibility_announce_collapse,
+ selectedBubbleContentDesc);
+ }
+ announceForAccessibility(msg);
+ }
+
private boolean isIconSizeOrPaddingUpdated(float newIconSize, float newBubbleBarPadding) {
return isIconSizeUpdated(newIconSize) || isPaddingUpdated(newBubbleBarPadding);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 74a673b..916b7b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -39,6 +39,7 @@
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
@@ -73,6 +74,7 @@
private TaskbarInsetsController mTaskbarInsetsController;
private View.OnClickListener mBubbleClickListener;
private View.OnClickListener mBubbleBarClickListener;
+ private BubbleView.Controller mBubbleViewController;
// These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
private final MultiValueAlpha mBubbleBarAlpha;
@@ -91,7 +93,7 @@
private BubbleBarViewAnimator mBubbleBarViewAnimator;
- private TimeSource mTimeSource = System::currentTimeMillis;
+ private final TimeSource mTimeSource = System::currentTimeMillis;
@Nullable
private BubbleBarBoundsChangeListener mBoundsChangeListener;
@@ -153,6 +155,31 @@
mBubbleBarController.updateBubbleBarLocation(location);
}
});
+
+ mBubbleViewController = new BubbleView.Controller() {
+ @Override
+ public BubbleBarLocation getBubbleBarLocation() {
+ return BubbleBarViewController.this.getBubbleBarLocation();
+ }
+
+ @Override
+ public void dismiss(BubbleView bubble) {
+ if (bubble.getBubble() != null) {
+ notifySysUiBubbleDismissed(bubble.getBubble());
+ }
+ onBubbleDismissed(bubble);
+ }
+
+ @Override
+ public void collapse() {
+ collapseBubbleBar();
+ }
+
+ @Override
+ public void updateBubbleBarLocation(BubbleBarLocation location) {
+ mBubbleBarController.updateBubbleBarLocation(location);
+ }
+ };
}
private void onBubbleClicked(BubbleView bubbleView) {
@@ -165,8 +192,7 @@
final String currentlySelected = mBubbleBarController.getSelectedBubbleKey();
if (mBarView.isExpanded() && Objects.equals(bubble.getKey(), currentlySelected)) {
// Tapping the currently selected bubble while expanded collapses the view.
- setExpanded(false);
- mBubbleStashController.stashBubbleBar();
+ collapseBubbleBar();
} else {
mBubbleBarController.showAndSelectBubble(bubble);
}
@@ -196,6 +222,11 @@
}
}
+ private void collapseBubbleBar() {
+ setExpanded(false);
+ mBubbleStashController.stashBubbleBar();
+ }
+
/** Notifies that the stash state is changing. */
public void onStashStateChanging() {
if (isAnimatingNewBubble()) {
@@ -220,7 +251,7 @@
return mBubbleBarTranslationY;
}
- float getBubbleBarCollapsedHeight() {
+ public float getBubbleBarCollapsedHeight() {
return mBarView.getBubbleBarCollapsedHeight();
}
@@ -356,7 +387,8 @@
int newIconSize;
int newPadding;
Resources res = mActivity.getResources();
- if (mBubbleStashController.isBubblesShowingOnHome()) {
+ if (mBubbleStashController.isBubblesShowingOnHome()
+ || mBubbleStashController.isTransientTaskBar()) {
newIconSize = getBubbleBarIconSizeFromDeviceProfile(res);
newPadding = getBubbleBarPaddingFromDeviceProfile(res);
} else {
@@ -440,6 +472,7 @@
public void removeBubble(BubbleBarBubble b) {
if (b != null) {
mBarView.removeBubble(b.getView());
+ b.getView().setController(null);
} else {
Log.w(TAG, "removeBubble, bubble was null!");
}
@@ -450,6 +483,8 @@
BubbleBarBubble removedBubble, boolean isExpanding, boolean suppressAnimation) {
mBarView.addBubbleAndRemoveBubble(addedBubble.getView(), removedBubble.getView());
addedBubble.getView().setOnClickListener(mBubbleClickListener);
+ addedBubble.getView().setController(mBubbleViewController);
+ removedBubble.getView().setController(null);
mBubbleDragController.setupBubbleView(addedBubble.getView());
if (!suppressAnimation) {
animateBubbleNotification(addedBubble, isExpanding, /* isUpdate= */ false);
@@ -464,6 +499,7 @@
mBarView.addBubble(b.getView());
b.getView().setOnClickListener(mBubbleClickListener);
mBubbleDragController.setupBubbleView(b.getView());
+ b.getView().setController(mBubbleViewController);
if (b instanceof BubbleBarOverflow) {
return;
@@ -473,7 +509,8 @@
// the bubble bar and handle are initialized as part of the first bubble animation.
// if the animation is suppressed, immediately stash or show the bubble bar to
// ensure they've been initialized.
- if (mTaskbarStashController.isInApp()) {
+ if (mTaskbarStashController.isInApp()
+ && mBubbleStashController.isTransientTaskBar()) {
mBubbleStashController.stashBubbleBarImmediate();
} else {
mBubbleStashController.showBubbleBarImmediate();
@@ -496,14 +533,15 @@
mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding);
return;
}
-
- if (mBubbleStashController.isBubblesShowingOnHome() && !isExpanding && !isExpanded()) {
+ boolean persistentTaskbarOrOnHome = mBubbleStashController.isBubblesShowingOnHome()
+ || !mBubbleStashController.isTransientTaskBar();
+ if (persistentTaskbarOrOnHome && !isExpanding && !isExpanded()) {
mBubbleBarViewAnimator.animateBubbleBarForCollapsed(bubble);
return;
}
- // only animate the new bubble if we're in an app and not auto expanding
- if (isInApp && !isExpanding && !isExpanded()) {
+ // only animate the new bubble if we're in an app, have handle view and not auto expanding
+ if (isInApp && !isExpanding && mBubbleStashController.getHasHandleView() && !isExpanded()) {
mBubbleBarViewAnimator.animateBubbleInForStashed(bubble);
}
}
@@ -564,6 +602,7 @@
/**
* Updates the dragged bubble view in the bubble bar view, and notifies SystemUI
* that a bubble is being dragged to dismiss.
+ *
* @param bubbleView dragged bubble view
*/
public void onBubbleDragStart(@NonNull BubbleView bubbleView) {
@@ -580,8 +619,8 @@
mSystemUiProxy.stopBubbleDrag(location, mBarView.getRestingTopPositionOnScreen());
}
- /** Notifies {@link BubbleBarView} that the dragged bubble was dismissed. */
- public void onBubbleDragDismissed(BubbleView bubble) {
+ /** Handle given bubble being dismissed */
+ public void onBubbleDismissed(BubbleView bubble) {
mBubbleBarController.onBubbleDismissed(bubble);
mBarView.removeBubble(bubble);
}
@@ -624,10 +663,9 @@
}
/**
- * Called when given bubble was dismissed. Notifies SystemUI
- * @param bubble dismissed bubble item
+ * Notify SystemUI that the given bubble has been dismissed.
*/
- public void onDismissBubble(@NonNull BubbleBarItem bubble) {
+ public void notifySysUiBubbleDismissed(@NonNull BubbleBarItem bubble) {
mSystemUiProxy.dragBubbleToDismiss(bubble.getKey(), mTimeSource.currentTimeMillis());
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index 03140fe..a5243fa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -16,19 +16,19 @@
package com.android.launcher3.taskbar.bubbles;
import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.RunnableList;
import java.io.PrintWriter;
+import java.util.Optional;
-/**
- * Hosts various bubble controllers to facilitate passing between one another.
- */
+/** Hosts various bubble controllers to facilitate passing between one another. */
public class BubbleControllers {
public final BubbleBarController bubbleBarController;
public final BubbleBarViewController bubbleBarViewController;
public final BubbleStashController bubbleStashController;
- public final BubbleStashedHandleViewController bubbleStashedHandleViewController;
+ public final Optional<BubbleStashedHandleViewController> bubbleStashedHandleViewController;
public final BubbleDragController bubbleDragController;
public final BubbleDismissController bubbleDismissController;
public final BubbleBarPinController bubbleBarPinController;
@@ -45,7 +45,7 @@
BubbleBarController bubbleBarController,
BubbleBarViewController bubbleBarViewController,
BubbleStashController bubbleStashController,
- BubbleStashedHandleViewController bubbleStashedHandleViewController,
+ Optional<BubbleStashedHandleViewController> bubbleStashedHandleViewController,
BubbleDragController bubbleDragController,
BubbleDismissController bubbleDismissController,
BubbleBarPinController bubbleBarPinController,
@@ -68,9 +68,15 @@
public void init(TaskbarControllers taskbarControllers) {
bubbleBarController.init(this,
taskbarControllers.navbarButtonsViewController::isImeVisible);
- bubbleBarViewController.init(taskbarControllers, this);
- bubbleStashedHandleViewController.init(taskbarControllers, this);
- bubbleStashController.init(taskbarControllers, this);
+ bubbleStashedHandleViewController.ifPresent(
+ controller -> controller.init(/* bubbleControllers = */ this));
+ bubbleStashController.init(
+ taskbarControllers.taskbarInsetsController,
+ bubbleBarViewController,
+ bubbleStashedHandleViewController.orElse(null),
+ taskbarControllers::runAfterInit
+ );
+ bubbleBarViewController.init(taskbarControllers, /* bubbleControllers = */ this);
bubbleDragController.init(/* bubbleControllers = */ this);
bubbleDismissController.init(/* bubbleControllers = */ this);
bubbleBarPinController.init(this);
@@ -93,7 +99,7 @@
* Cleans up all controllers.
*/
public void onDestroy() {
- bubbleStashedHandleViewController.onDestroy();
+ bubbleStashedHandleViewController.ifPresent(BubbleStashedHandleViewController::onDestroy);
bubbleBarController.onDestroy();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index 6a63da8..5eebbd8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -143,7 +143,7 @@
if (mMagnetizedObject.getUnderlyingObject() instanceof BubbleView) {
BubbleView bubbleView = (BubbleView) mMagnetizedObject.getUnderlyingObject();
if (bubbleView.getBubble() != null) {
- mBubbleBarViewController.onDismissBubble(bubbleView.getBubble());
+ mBubbleBarViewController.notifySysUiBubbleDismissed(bubbleView.getBubble());
}
} else if (mMagnetizedObject.getUnderlyingObject() instanceof BubbleBarView) {
mBubbleBarViewController.onDismissAllBubbles();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 8316b5b..656a266 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -153,7 +153,7 @@
@Override
protected void onDragDismiss() {
mBubblePinController.onDragEnd();
- mBubbleBarViewController.onBubbleDragDismissed(bubbleView);
+ mBubbleBarViewController.onBubbleDismissed(bubbleView);
mBubbleBarViewController.onBubbleDragEnd();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
index a77e685..1341b53 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubblePinController.kt
@@ -27,6 +27,7 @@
import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.wm.shell.common.bubbles.BaseBubblePinController
import com.android.wm.shell.common.bubbles.BubbleBarLocation
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
deleted file mode 100644
index 74f58ac..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar.bubbles;
-
-import static java.lang.Math.abs;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.annotation.Nullable;
-import android.view.InsetsController;
-import android.view.MotionEvent;
-import android.view.View;
-
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.taskbar.StashedHandleViewController;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarInsetsController;
-import com.android.launcher3.taskbar.TaskbarStashController;
-import com.android.launcher3.util.MultiPropertyFactory;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.shared.animation.PhysicsAnimator;
-
-import java.io.PrintWriter;
-
-/**
- * Coordinates between controllers such as BubbleBarView and BubbleHandleViewController to
- * create a cohesive animation between stashed/unstashed states.
- */
-public class BubbleStashController {
-
- private static final String TAG = "BubbleStashController";
-
- /**
- * How long to stash/unstash.
- */
- public static final long BAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE;
-
- /**
- * The scale bubble bar animates to when being stashed.
- */
- private static final float STASHED_BAR_SCALE = 0.5f;
-
- protected final TaskbarActivityContext mActivity;
-
- // Initialized in init.
- private TaskbarControllers mControllers;
- private TaskbarInsetsController mTaskbarInsetsController;
- private BubbleBarViewController mBarViewController;
- private BubbleStashedHandleViewController mHandleViewController;
- private TaskbarStashController mTaskbarStashController;
-
- private MultiPropertyFactory.MultiProperty mIconAlphaForStash;
- private AnimatedFloat mIconScaleForStash;
- private AnimatedFloat mIconTranslationYForStash;
- private MultiPropertyFactory.MultiProperty mBubbleStashedHandleAlpha;
-
- private boolean mRequestedStashState;
- private boolean mRequestedExpandedState;
-
- private boolean mIsStashed;
- private int mStashedHeight;
- private int mUnstashedHeight;
- private boolean mBubblesShowingOnHome;
- private boolean mBubblesShowingOnOverview;
- private boolean mIsSysuiLocked;
-
- private final float mHandleCenterFromScreenBottom;
-
- @Nullable
- private AnimatorSet mAnimator;
-
- public BubbleStashController(TaskbarActivityContext activity) {
- mActivity = activity;
- // the handle is centered within the stashed taskbar area
- mHandleCenterFromScreenBottom =
- mActivity.getResources().getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f;
- }
-
- public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
- mControllers = controllers;
- mTaskbarInsetsController = controllers.taskbarInsetsController;
- mBarViewController = bubbleControllers.bubbleBarViewController;
- mHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
- mTaskbarStashController = controllers.taskbarStashController;
-
- mIconAlphaForStash = mBarViewController.getBubbleBarAlpha().get(0);
- mIconScaleForStash = mBarViewController.getBubbleBarScale();
- mIconTranslationYForStash = mBarViewController.getBubbleBarTranslationY();
-
- mBubbleStashedHandleAlpha = mHandleViewController.getStashedHandleAlpha().get(
- StashedHandleViewController.ALPHA_INDEX_STASHED);
-
- mStashedHeight = mHandleViewController.getStashedHeight();
- mUnstashedHeight = mHandleViewController.getUnstashedHeight();
- }
-
- /**
- * Returns the touchable height of the bubble bar based on it's stashed state.
- */
- public int getTouchableHeight() {
- return mIsStashed ? mStashedHeight : mUnstashedHeight;
- }
-
- /**
- * Returns whether the bubble bar is currently stashed.
- */
- public boolean isStashed() {
- return mIsStashed;
- }
-
- /**
- * Animates the handle (or bubble bar depending on state) to be visible after the device is
- * unlocked.
- *
- * <p>Normally either the bubble bar or the handle is visible,
- * and {@link #showBubbleBar(boolean)} and {@link #stashBubbleBar()} are used to transition
- * between these two states. But the transition from the state where both the bar and handle
- * are invisible is slightly different.
- */
- private void animateAfterUnlock() {
- AnimatorSet animatorSet = new AnimatorSet();
- if (mBubblesShowingOnHome || mBubblesShowingOnOverview) {
- mIsStashed = false;
- animatorSet.playTogether(mIconScaleForStash.animateToValue(1),
- mIconTranslationYForStash.animateToValue(getBubbleBarTranslationY()),
- mIconAlphaForStash.animateToValue(1));
- } else {
- mIsStashed = true;
- animatorSet.playTogether(mBubbleStashedHandleAlpha.animateToValue(1));
- }
-
- animatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onIsStashedChanged();
- }
- });
- animatorSet.setDuration(BAR_STASH_DURATION).start();
- }
-
- /**
- * Called when launcher enters or exits the home page. Bubbles are unstashed on home.
- */
- public void setBubblesShowingOnHome(boolean onHome) {
- if (mBubblesShowingOnHome != onHome) {
- mBubblesShowingOnHome = onHome;
-
- if (!mBarViewController.hasBubbles()) {
- // if there are no bubbles, there's nothing to show, so just return.
- return;
- }
-
- if (mBubblesShowingOnHome) {
- showBubbleBar(/* expanded= */ false);
- // When transitioning from app to home the stash animator may already have been
- // created, so we need to animate the bubble bar here to align with hotseat.
- if (!mIsStashed) {
- mIconTranslationYForStash.animateToValue(getBubbleBarTranslationYForHotseat())
- .start();
- }
- // If the bubble bar is already unstashed, the taskbar touchable region won't be
- // updated correctly, so force an update here.
- mControllers.runAfterInit(() ->
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged());
- } else if (!mBarViewController.isExpanded()) {
- stashBubbleBar();
- }
- }
- }
-
- /** Whether bubbles are showing on the launcher home page. */
- public boolean isBubblesShowingOnHome() {
- boolean hasBubbles = mBarViewController != null && mBarViewController.hasBubbles();
- return mBubblesShowingOnHome && hasBubbles;
- }
-
- // TODO: when tapping on an app in overview, this is a bit delayed compared to taskbar stashing
- /** Called when launcher enters or exits overview. Bubbles are unstashed on overview. */
- public void setBubblesShowingOnOverview(boolean onOverview) {
- if (mBubblesShowingOnOverview != onOverview) {
- mBubblesShowingOnOverview = onOverview;
- if (!mBubblesShowingOnOverview && !mBarViewController.isExpanded()) {
- stashBubbleBar();
- } else {
- // When transitioning to overview the stash animator may already have been
- // created, so we need to animate the bubble bar here to align with taskbar.
- mIconTranslationYForStash.animateToValue(getBubbleBarTranslationYForTaskbar())
- .start();
- }
- }
- }
-
- /** Whether bubbles are showing on Overview. */
- public boolean isBubblesShowingOnOverview() {
- return mBubblesShowingOnOverview;
- }
-
- /** Called when sysui locked state changes, when locked, bubble bar is stashed. */
- public void onSysuiLockedStateChange(boolean isSysuiLocked) {
- if (isSysuiLocked != mIsSysuiLocked) {
- mIsSysuiLocked = isSysuiLocked;
- if (!mIsSysuiLocked && mBarViewController.hasBubbles()) {
- animateAfterUnlock();
- }
- }
- }
-
- /**
- * Stashes the bubble bar if allowed based on other state (e.g. on home and overview the
- * bar does not stash).
- */
- public void stashBubbleBar() {
- mRequestedStashState = true;
- mRequestedExpandedState = false;
- updateStashedAndExpandedState();
- }
-
- /**
- * Shows the bubble bar, and expands bubbles depending on {@param expandBubbles}.
- */
- public void showBubbleBar(boolean expandBubbles) {
- mRequestedStashState = false;
- mRequestedExpandedState = expandBubbles;
- updateStashedAndExpandedState();
- }
-
- private void updateStashedAndExpandedState() {
- if (mBarViewController.isHiddenForNoBubbles()) {
- // If there are no bubbles the bar and handle are invisible, nothing to do here.
- return;
- }
- boolean isStashed = mRequestedStashState
- && !mBubblesShowingOnHome
- && !mBubblesShowingOnOverview;
- if (mIsStashed != isStashed) {
- // notify the view controller that the stash state is about to change so that it can
- // cancel an ongoing animation if there is one.
- // note that this has to be called before updating mIsStashed with the new value,
- // otherwise interrupting an ongoing animation may update it again with the wrong state
- mBarViewController.onStashStateChanging();
- mIsStashed = isStashed;
- if (mAnimator != null) {
- mAnimator.cancel();
- }
- mAnimator = createStashAnimator(mIsStashed, BAR_STASH_DURATION);
- mAnimator.start();
- onIsStashedChanged();
- }
- if (mBarViewController.isExpanded() != mRequestedExpandedState) {
- mBarViewController.setExpanded(mRequestedExpandedState);
- }
- }
-
- /**
- * Create a stash animation.
- *
- * @param isStashed whether it's a stash animation or an unstash animation
- * @param duration duration of the animation
- * @return the animation
- */
- private AnimatorSet createStashAnimator(boolean isStashed, long duration) {
- AnimatorSet animatorSet = new AnimatorSet();
-
- AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
- // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
- AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
- AnimatorSet secondHalfAnimatorSet = new AnimatorSet();
-
- final float firstHalfDurationScale;
- final float secondHalfDurationScale;
-
- if (isStashed) {
- firstHalfDurationScale = 0.75f;
- secondHalfDurationScale = 0.5f;
-
- fullLengthAnimatorSet.play(
- mIconTranslationYForStash.animateToValue(getStashTranslation()));
-
- firstHalfAnimatorSet.playTogether(
- mIconAlphaForStash.animateToValue(0),
- mIconScaleForStash.animateToValue(STASHED_BAR_SCALE));
- secondHalfAnimatorSet.playTogether(
- mBubbleStashedHandleAlpha.animateToValue(1));
- } else {
- firstHalfDurationScale = 0.5f;
- secondHalfDurationScale = 0.75f;
-
- final float translationY = getBubbleBarTranslationY();
-
- fullLengthAnimatorSet.playTogether(
- mIconScaleForStash.animateToValue(1),
- mIconTranslationYForStash.animateToValue(translationY));
-
- firstHalfAnimatorSet.playTogether(
- mBubbleStashedHandleAlpha.animateToValue(0)
- );
- secondHalfAnimatorSet.playTogether(
- mIconAlphaForStash.animateToValue(1)
- );
- }
-
- fullLengthAnimatorSet.play(mHandleViewController.createRevealAnimToIsStashed(isStashed));
-
- fullLengthAnimatorSet.setDuration(duration);
- firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale));
- secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
- secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
-
- animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
- secondHalfAnimatorSet);
- animatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimator = null;
- mControllers.runAfterInit(() -> {
- if (isStashed) {
- mBarViewController.setExpanded(false);
- }
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
- });
- }
- });
- return animatorSet;
- }
-
- private float getStashTranslation() {
- return (mUnstashedHeight - mStashedHeight) / 2f;
- }
-
- private void onIsStashedChanged() {
- mControllers.runAfterInit(() -> {
- mHandleViewController.onIsStashedChanged();
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
- });
- }
-
- public float getBubbleBarTranslationYForTaskbar() {
- return -mActivity.getDeviceProfile().taskbarBottomMargin;
- }
-
- private float getBubbleBarTranslationYForHotseat() {
- final float hotseatBottomSpace = mActivity.getDeviceProfile().hotseatBarBottomSpacePx;
- final float hotseatCellHeight = mActivity.getDeviceProfile().hotseatCellHeightPx;
- return -hotseatBottomSpace - hotseatCellHeight + mUnstashedHeight - abs(
- hotseatCellHeight - mUnstashedHeight) / 2;
- }
-
- public float getBubbleBarTranslationY() {
- // If we're on home, adjust the translation so the bubble bar aligns with hotseat.
- // Otherwise we're either showing in an app or in overview. In either case adjust it so
- // the bubble bar aligns with the taskbar.
- return mBubblesShowingOnHome ? getBubbleBarTranslationYForHotseat()
- : getBubbleBarTranslationYForTaskbar();
- }
-
- /**
- * The difference on the Y axis between the center of the handle and the center of the bubble
- * bar.
- */
- public float getDiffBetweenHandleAndBarCenters() {
- // the difference between the centers of the handle and the bubble bar is the difference
- // between their distance from the bottom of the screen.
-
- float barCenter = mBarViewController.getBubbleBarCollapsedHeight() / 2f;
- return mHandleCenterFromScreenBottom - barCenter;
- }
-
- /** The distance the handle moves as part of the new bubble animation. */
- public float getStashedHandleTranslationForNewBubbleAnimation() {
- // the should move up to the top of the stashed taskbar area. it is centered within it so
- // it should move the same distance as it is away from the bottom.
- return -mHandleCenterFromScreenBottom;
- }
-
- /** Checks whether the motion event is over the stash handle. */
- public boolean isEventOverStashHandle(MotionEvent ev) {
- return mHandleViewController.isEventOverHandle(ev);
- }
-
- /** Set a bubble bar location */
- public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
- mHandleViewController.setBubbleBarLocation(bubbleBarLocation);
- }
-
- /** Returns the [PhysicsAnimator] for the stashed handle view. */
- public PhysicsAnimator<View> getStashedHandlePhysicsAnimator() {
- return mHandleViewController.getPhysicsAnimator();
- }
-
- /** Notifies taskbar that it should update its touchable region. */
- public void updateTaskbarTouchRegion() {
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
- }
-
- /** Shows the bubble bar immediately without animation. */
- public void showBubbleBarImmediate() {
- mHandleViewController.setTranslationYForSwipe(0);
- mIconTranslationYForStash.updateValue(getBubbleBarTranslationY());
- mIconAlphaForStash.setValue(1);
- mIconScaleForStash.updateValue(1);
- mIsStashed = false;
- onIsStashedChanged();
- }
-
- /** Stashes the bubble bar immediately without animation. */
- public void stashBubbleBarImmediate() {
- mHandleViewController.setTranslationYForSwipe(0);
- mBubbleStashedHandleAlpha.setValue(1);
- mIconAlphaForStash.setValue(0);
- mIconTranslationYForStash.updateValue(getStashTranslation());
- mIconScaleForStash.updateValue(STASHED_BAR_SCALE);
- mIsStashed = true;
- onIsStashedChanged();
- }
-
- /**
- * Updates the values of the internal animators after the new bubble animation was interrupted
- *
- * @param isStashed whether the current state should be stashed
- * @param bubbleBarTranslationY the current bubble bar translation. this is only used if the
- * bubble bar is showing to ensure that the stash animator runs
- * smoothly.
- */
- public void onNewBubbleAnimationInterrupted(boolean isStashed, float bubbleBarTranslationY) {
- if (isStashed) {
- mBubbleStashedHandleAlpha.setValue(1);
- mIconAlphaForStash.setValue(0);
- mIconScaleForStash.updateValue(STASHED_BAR_SCALE);
- mIconTranslationYForStash.updateValue(getStashTranslation());
- } else {
- mBubbleStashedHandleAlpha.setValue(0);
- mHandleViewController.setTranslationYForSwipe(0);
- mIconAlphaForStash.setValue(1);
- mIconScaleForStash.updateValue(1);
- mIconTranslationYForStash.updateValue(bubbleBarTranslationY);
- }
- mIsStashed = isStashed;
- onIsStashedChanged();
- }
-
- /** Set the translation Y for the stashed handle. */
- public void setHandleTranslationY(float ty) {
- mHandleViewController.setTranslationYForSwipe(ty);
- }
-
- /** Dumps the state of BubbleStashController. */
- public void dump(PrintWriter pw) {
- pw.println("Bubble stash controller state:");
- pw.println(" mIsStashed: " + mIsStashed);
- pw.println(" mBubblesShowingOnOverview: " + mBubblesShowingOnOverview);
- pw.println(" mBubblesShowingOnHome: " + mBubblesShowingOnHome);
- pw.println(" mIsSysuiLocked: " + mIsSysuiLocked);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 91103d7..52f5a29 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -33,7 +33,7 @@
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.taskbar.StashedHandleView;
import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
@@ -79,7 +79,8 @@
mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
}
- public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
+ /** Initialize controller. */
+ public void init(BubbleControllers bubbleControllers) {
mBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleStashController = bubbleControllers.bubbleStashController;
@@ -117,7 +118,7 @@
public Rect getSampledRegion(View sampledView) {
return mStashedHandleView.getSampledRegion();
}
- }, Executors.UI_HELPER_EXECUTOR);
+ }, Executors.MAIN_EXECUTOR, Executors.UI_HELPER_EXECUTOR);
mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
updateBounds(mBarViewController.getBubbleBarLocation()));
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index acb6b4e..09da3e0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -20,22 +20,22 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.launcher3.R;
import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.bubbles.BubbleInfo;
// TODO: (b/276978250) This is will be similar to WMShell's BadgedImageView, it'd be nice to share.
@@ -67,12 +67,22 @@
// The current scale value of the dot
private float mDotScale;
+ private boolean mProvideShadowOutline = true;
+
// TODO: (b/273310265) handle RTL
// Whether the bubbles are positioned on the left or right side of the screen
private boolean mOnLeft = false;
private BubbleBarItem mBubble;
+ private Bitmap mIcon;
+
+ @Nullable
+ private Controller mController;
+
+ @Nullable
+ private BubbleBarBubbleIconsFactory mIconFactory = null;
+
public BubbleView(Context context) {
this(context, null);
}
@@ -99,25 +109,14 @@
setFocusable(true);
setClickable(true);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- BubbleView.this.getOutline(outline);
- }
- });
- }
-
- private void getOutline(Outline outline) {
- updateBubbleSizeAndDotRender();
- final int normalizedSize = IconNormalizer.getNormalizedCircleSize(mBubbleSize);
- final int inset = (mBubbleSize - normalizedSize) / 2;
- outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
}
private void updateBubbleSizeAndDotRender() {
int updatedBubbleSize = Math.min(getWidth(), getHeight());
if (updatedBubbleSize == mBubbleSize) return;
mBubbleSize = updatedBubbleSize;
+ mIconFactory = new BubbleBarBubbleIconsFactory(mContext, mBubbleSize);
+ updateBubbleIcon();
if (mBubble == null || mBubble instanceof BubbleBarOverflow) return;
Path dotPath = ((BubbleBarBubble) mBubble).getDotPath();
mDotRenderer = new DotRenderer(mBubbleSize, dotPath, DEFAULT_PATH_SIZE);
@@ -180,10 +179,63 @@
mDotRenderer.draw(canvas, mDrawParams);
}
+ @Override
+ public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfoInternal(info);
+ info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+ if (mBubble instanceof BubbleBarBubble) {
+ info.addAction(AccessibilityNodeInfo.ACTION_DISMISS);
+ }
+ if (mController != null) {
+ if (mController.getBubbleBarLocation().isOnLeft(isLayoutRtl())) {
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_move_right,
+ getResources().getString(R.string.bubble_bar_action_move_right)));
+ } else {
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_move_left,
+ getResources().getString(R.string.bubble_bar_action_move_left)));
+ }
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (super.performAccessibilityActionInternal(action, arguments)) {
+ return true;
+ }
+ if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) {
+ if (mController != null) {
+ mController.collapse();
+ }
+ return true;
+ }
+ if (action == AccessibilityNodeInfo.ACTION_DISMISS) {
+ if (mController != null) {
+ mController.dismiss(this);
+ }
+ return true;
+ }
+ if (action == R.id.action_move_left) {
+ if (mController != null) {
+ mController.updateBubbleBarLocation(BubbleBarLocation.LEFT);
+ }
+ }
+ if (action == R.id.action_move_right) {
+ if (mController != null) {
+ mController.updateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ }
+ }
+ return false;
+ }
+
+ void setController(@Nullable Controller controller) {
+ mController = controller;
+ }
+
/** Sets the bubble being rendered in this view. */
public void setBubble(BubbleBarBubble bubble) {
mBubble = bubble;
- mBubbleIcon.setImageBitmap(bubble.getIcon());
+ mIcon = bubble.getIcon();
+ updateBubbleIcon();
mAppIcon.setImageBitmap(bubble.getBadge());
mDotColor = bubble.getDotColor();
mDotRenderer = new DotRenderer(mBubbleSize, bubble.getDotPath(), DEFAULT_PATH_SIZE);
@@ -199,6 +251,18 @@
setContentDescription(contentDesc);
}
+ private void updateBubbleIcon() {
+ Bitmap icon = null;
+ if (mIcon != null) {
+ icon = mIcon;
+ if (mIconFactory != null) {
+ BitmapDrawable iconDrawable = new BitmapDrawable(getResources(), icon);
+ icon = mIconFactory.createShadowedIconBitmap(iconDrawable, /* scale = */ 1f);
+ }
+ }
+ mBubbleIcon.setImageBitmap(icon);
+ }
+
/**
* Sets that this bubble represents the overflow. The overflow appears in the list of bubbles
* but does not represent app content, instead it shows recent bubbles that couldn't fit into
@@ -207,7 +271,8 @@
*/
public void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) {
mBubble = overflow;
- mBubbleIcon.setImageBitmap(bitmap);
+ mIcon = bitmap;
+ updateBubbleIcon();
mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge
setContentDescription(getResources().getString(R.string.bubble_bar_overflow_description));
}
@@ -229,18 +294,9 @@
}
}
- void updateBadgeVisibility(boolean show) {
- if (mBubble instanceof BubbleBarOverflow) {
- // The overflow bubble does not have a badge, so just bail.
- return;
- }
- BubbleBarBubble bubble = (BubbleBarBubble) mBubble;
- Bitmap appBadgeBitmap = bubble.getBadge();
- int translationX = mOnLeft
- ? -(bubble.getIcon().getWidth() - appBadgeBitmap.getWidth())
- : 0;
- mAppIcon.setTranslationX(translationX);
- mAppIcon.setVisibility(show ? VISIBLE : GONE);
+ void setBadgeScale(float fraction) {
+ mAppIcon.setScaleX(fraction);
+ mAppIcon.setScaleY(fraction);
}
boolean hasUnseenContent() {
@@ -337,4 +393,19 @@
String toString = mBubble != null ? mBubble.getKey() : "null";
return "BubbleView{" + toString + "}";
}
+
+ /** Interface for BubbleView to communicate with its controller */
+ public interface Controller {
+ /** Get current bubble bar {@link BubbleBarLocation} */
+ BubbleBarLocation getBubbleBarLocation();
+
+ /** This bubble should be dismissed */
+ void dismiss(BubbleView bubble);
+
+ /** Collapse the bubble bar */
+ void collapse();
+
+ /** Request bubble bar location to be updated to the given location */
+ void updateBubbleBarLocation(BubbleBarLocation 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 feff9fd..0a0cfd0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -26,8 +26,8 @@
import com.android.launcher3.R
import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
import com.android.launcher3.taskbar.bubbles.BubbleBarView
-import com.android.launcher3.taskbar.bubbles.BubbleStashController
import com.android.launcher3.taskbar.bubbles.BubbleView
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.wm.shell.shared.animation.PhysicsAnimator
/** Handles animations for bubble bar bubbles. */
@@ -138,19 +138,20 @@
// handle. when the handle becomes invisible and we start animating in the bubble bar,
// the translation y is offset by this value to make the transition from the handle to the
// bar smooth.
- val offset = bubbleStashController.diffBetweenHandleAndBarCenters
- val stashedHandleTranslationY =
- bubbleStashController.stashedHandleTranslationForNewBubbleAnimation
+ val offset: Float = bubbleStashController.getDiffBetweenHandleAndBarCenters()
+ val stashedHandleTranslationY: Float =
+ bubbleStashController.getStashedHandleTranslationForNewBubbleAnimation()
// this is the total distance that both the stashed handle and the bubble will be traveling
// at the end of the animation the bubble bar will be positioned in the same place when it
// shows while we're in an app.
val totalTranslationY = bubbleStashController.bubbleBarTranslationYForTaskbar + offset
- val animator = bubbleStashController.stashedHandlePhysicsAnimator
+ val animator = bubbleStashController.getStashedHandlePhysicsAnimator() ?: return@Runnable
animator.setDefaultSpringConfig(springConfig)
animator.spring(DynamicAnimation.TRANSLATION_Y, totalTranslationY)
animator.addUpdateListener { handle, values ->
- val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener
+ val ty: Float =
+ values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener
when {
ty >= stashedHandleTranslationY -> {
// we're in the first leg of the animation. only animate the handle. the bubble
@@ -226,13 +227,13 @@
*/
private fun buildBubbleBarToHandleAnimation() = Runnable {
if (animatingBubble == null) return@Runnable
- val offset = bubbleStashController.diffBetweenHandleAndBarCenters
+ val offset = bubbleStashController.getStashedHandleTranslationForNewBubbleAnimation()
val stashedHandleTranslationY =
- bubbleStashController.stashedHandleTranslationForNewBubbleAnimation
+ bubbleStashController.getStashedHandleTranslationForNewBubbleAnimation()
// this is the total distance that both the stashed handle and the bar will be traveling
val totalTranslationY = bubbleStashController.bubbleBarTranslationYForTaskbar + offset
bubbleStashController.setHandleTranslationY(totalTranslationY)
- val animator = bubbleStashController.stashedHandlePhysicsAnimator
+ val animator = bubbleStashController.getStashedHandlePhysicsAnimator() ?: return@Runnable
animator.setDefaultSpringConfig(springConfig)
animator.spring(DynamicAnimation.TRANSLATION_Y, 0f)
animator.addUpdateListener { handle, values ->
@@ -363,7 +364,7 @@
/** Handles touching the animating bubble bar. */
fun onBubbleBarTouchedWhileAnimating() {
PhysicsAnimator.getInstance(bubbleBarView).cancelIfRunning()
- bubbleStashController.stashedHandlePhysicsAnimator.cancelIfRunning()
+ bubbleStashController.getStashedHandlePhysicsAnimator().cancelIfRunning()
val hideAnimation = animatingBubble?.hideAnimation ?: return
scheduler.cancel(hideAnimation)
bubbleBarView.onAnimatingBubbleCompleted()
@@ -376,7 +377,7 @@
val hideAnimation = animatingBubble?.hideAnimation ?: return
scheduler.cancel(hideAnimation)
animatingBubble = null
- bubbleStashController.stashedHandlePhysicsAnimator.cancel()
+ bubbleStashController.getStashedHandlePhysicsAnimator().cancelIfRunning()
bubbleBarView.onAnimatingBubbleCompleted()
bubbleBarView.relativePivotY = 1f
bubbleStashController.onNewBubbleAnimationInterrupted(
@@ -385,8 +386,8 @@
)
}
- private fun <T> PhysicsAnimator<T>.cancelIfRunning() {
- if (isRunning()) cancel()
+ private fun <T> PhysicsAnimator<T>?.cancelIfRunning() {
+ if (this?.isRunning() == true) cancel()
}
private fun ObjectAnimator.withDuration(duration: Long): ObjectAnimator {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
new file mode 100644
index 0000000..0f43744
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -0,0 +1,185 @@
+/*
+ * 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.stashing
+
+import android.view.InsetsController
+import android.view.MotionEvent
+import android.view.View
+import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+import java.io.PrintWriter
+
+/** StashController that defines stashing behaviour for the taskbar modes. */
+interface BubbleStashController {
+
+ /**
+ * Abstraction on the task bar activity context to only provide the dimensions required for
+ * [BubbleBarView] translation Y computation.
+ */
+ interface TaskbarHotseatDimensionsProvider {
+
+ /** Provides taskbar bottom space in pixels. */
+ fun getTaskbarBottomSpace(): Int
+
+ /** Provides taskbar height in pixels. */
+ fun getTaskbarHeight(): Int
+
+ /** Provides hotseat bottom space in pixels. */
+ fun getHotseatBottomSpace(): Int
+
+ /** Provides hotseat height in pixels. */
+ fun getHotseatHeight(): Int
+ }
+
+ /** Execute passed action only after controllers are initiated. */
+ interface ControllersAfterInitAction {
+ /** Execute action after controllers are initiated. */
+ fun runAfterInit(action: Runnable)
+ }
+
+ /** Whether bubble bar is currently stashed */
+ val isStashed: Boolean
+
+ /** Whether launcher enters or exits the home page. */
+ var isBubblesShowingOnHome: Boolean
+
+ /** Whether launcher enters or exits the overview page. */
+ var isBubblesShowingOnOverview: Boolean
+
+ /** Updated when sysui locked state changes, when locked, bubble bar is not shown. */
+ var isSysuiLocked: Boolean
+
+ /** Whether there is a transient taskbar mode */
+ val isTransientTaskBar: Boolean
+
+ /** Whether stash control has a handle view */
+ val hasHandleView: Boolean
+
+ /** Initialize controller */
+ fun init(
+ taskbarInsetsController: TaskbarInsetsController,
+ bubbleBarViewController: BubbleBarViewController,
+ bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
+ controllersAfterInitAction: ControllersAfterInitAction
+ )
+
+ /** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
+ fun showBubbleBarImmediate()
+
+ /** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
+ fun showBubbleBarImmediate(bubbleBarTranslationY: Float)
+
+ /** Stashes the bubble bar immediately without animation. */
+ fun stashBubbleBarImmediate()
+
+ /** Returns the touchable height of the bubble bar based on it's stashed state. */
+ fun getTouchableHeight(): Int
+
+ /** Whether bubble bar is currently visible */
+ fun isBubbleBarVisible(): Boolean
+
+ /**
+ * Updates the values of the internal animators after the new bubble animation was interrupted
+ *
+ * @param isStashed whether the current state should be stashed
+ * @param bubbleBarTranslationY the current bubble bar translation. this is only used if the
+ * bubble bar is showing to ensure that the stash animator runs smoothly.
+ */
+ fun onNewBubbleAnimationInterrupted(isStashed: Boolean, bubbleBarTranslationY: Float)
+
+ /** Checks whether the motion event is over the stash handle or bubble bar. */
+ fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean
+
+ /** Set a bubble bar location */
+ fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation)
+
+ /**
+ * Stashes the bubble bar (transform to the handle view), or just shrink width of the expanded
+ * bubble bar based on the controller implementation.
+ */
+ fun stashBubbleBar()
+
+ /** Shows the bubble bar, and expands bubbles depending on [expandBubbles]. */
+ fun showBubbleBar(expandBubbles: Boolean)
+
+ // TODO(b/354218264): Move to BubbleBarViewAnimator
+ /**
+ * The difference on the Y axis between the center of the handle and the center of the bubble
+ * bar.
+ */
+ fun getDiffBetweenHandleAndBarCenters(): Float
+
+ // TODO(b/354218264): Move to BubbleBarViewAnimator
+ /** The distance the handle moves as part of the new bubble animation. */
+ fun getStashedHandleTranslationForNewBubbleAnimation(): Float
+
+ // TODO(b/354218264): Move to BubbleBarViewAnimator
+ /** Returns the [PhysicsAnimator] for the stashed handle view. */
+ fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>?
+
+ // TODO(b/354218264): Move to BubbleBarViewAnimator
+ /** Notifies taskbar that it should update its touchable region. */
+ fun updateTaskbarTouchRegion()
+
+ // TODO(b/354218264): Move to BubbleBarViewAnimator
+ /** Set the translation Y for the stashed handle. */
+ fun setHandleTranslationY(translationY: Float)
+
+ /**
+ * Returns bubble bar Y position according to [isBubblesShowingOnHome] and
+ * [isBubblesShowingOnOverview] values. Default implementation only analyse
+ * [isBubblesShowingOnHome] and return translationY to align with the hotseat vertical center.
+ * For Other cases align bubbles with the taskbar.
+ */
+ val bubbleBarTranslationY: Float
+ get() =
+ if (isBubblesShowingOnHome) {
+ bubbleBarTranslationYForHotseat
+ } else {
+ bubbleBarTranslationYForTaskbar
+ }
+
+ /** Translation Y to align the bubble bar with the hotseat. */
+ val bubbleBarTranslationYForTaskbar: Float
+
+ /** Return translation Y to align the bubble bar with the taskbar. */
+ val bubbleBarTranslationYForHotseat: Float
+
+ /** Dumps the state of BubbleStashController. */
+ fun dump(pw: PrintWriter) {
+ pw.println("Bubble stash controller state:")
+ pw.println(" isStashed: $isStashed")
+ pw.println(" isBubblesShowingOnOverview: $isBubblesShowingOnOverview")
+ pw.println(" isBubblesShowingOnHome: $isBubblesShowingOnHome")
+ pw.println(" isSysuiLocked: $isSysuiLocked")
+ }
+
+ companion object {
+ /** How long to stash/unstash. */
+ const val BAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE.toLong()
+
+ /** How long to translate Y coordinate of the BubbleBar. */
+ const val BAR_TRANSLATION_DURATION = 300L
+
+ /** The scale bubble bar animates to when being stashed. */
+ const val STASHED_BAR_SCALE = 0.5f
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
new file mode 100644
index 0000000..a55763b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/DeviceProfileDimensionsProviderAdapter.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.stashing
+
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
+
+/**
+ * Implementation of the [TaskbarHotseatDimensionsProvider] that take sizes from the
+ * [DeviceProfile].
+ */
+class DeviceProfileDimensionsProviderAdapter(
+ private val taskbarActivityContext: TaskbarActivityContext
+) : TaskbarHotseatDimensionsProvider {
+ override fun getTaskbarBottomSpace(): Int = deviceProfile().taskbarBottomMargin
+
+ override fun getTaskbarHeight(): Int = deviceProfile().taskbarHeight
+
+ override fun getHotseatBottomSpace(): Int = deviceProfile().hotseatBarBottomSpacePx
+
+ override fun getHotseatHeight(): Int = deviceProfile().hotseatCellHeightPx
+
+ private fun deviceProfile(): DeviceProfile = taskbarActivityContext.deviceProfile
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
new file mode 100644
index 0000000..62fe221
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -0,0 +1,232 @@
+/*
+ * 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.stashing
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.view.MotionEvent
+import android.view.View
+import com.android.launcher3.anim.AnimatedFloat
+import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+
+class PersistentBubbleStashController(
+ private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
+) : BubbleStashController {
+
+ private lateinit var taskbarInsetsController: TaskbarInsetsController
+ private lateinit var bubbleBarViewController: BubbleBarViewController
+ private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
+ private lateinit var bubbleBarAlphaAnimator: MultiPropertyFactory<View>.MultiProperty
+ private lateinit var bubbleBarScaleAnimator: AnimatedFloat
+ private lateinit var controllersAfterInitAction: ControllersAfterInitAction
+
+ override var isBubblesShowingOnHome: Boolean = false
+ set(onHome) {
+ if (field == onHome) return
+ field = onHome
+ if (!bubbleBarViewController.hasBubbles()) {
+ // if there are no bubbles, there's nothing to show, so just return.
+ return
+ }
+ if (onHome) {
+ // When transition to home we should show collapse the bubble bar
+ updateExpandedState(expand = false)
+ }
+ animateBubbleBarY()
+ bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
+ }
+
+ override var isBubblesShowingOnOverview: Boolean = false
+ set(onOverview) {
+ if (field == onOverview) return
+ field = onOverview
+ if (!onOverview) {
+ // When transition from overview we should show collapse the bubble bar
+ updateExpandedState(expand = false)
+ }
+ bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
+ }
+
+ override var isSysuiLocked: Boolean = false
+ set(isLocked) {
+ if (field == isLocked) return
+ field = isLocked
+ if (!isLocked && bubbleBarViewController.hasBubbles()) {
+ animateAfterUnlock()
+ }
+ }
+
+ override var isTransientTaskBar: Boolean = false
+
+ /** When the bubble bar is shown for the persistent task bar, there is no handle view. */
+ override val hasHandleView: Boolean = false
+
+ /** For persistent task bar we never stash the bubble bar */
+ override val isStashed: Boolean = false
+
+ override val bubbleBarTranslationYForTaskbar: Float
+ get() {
+ val taskbarBottomMargin = taskbarHotseatDimensionsProvider.getTaskbarBottomSpace()
+ val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
+ val taskbarHeight = taskbarHotseatDimensionsProvider.getTaskbarHeight()
+ return -taskbarBottomMargin - (taskbarHeight - bubbleBarHeight) / 2f
+ }
+
+ override val bubbleBarTranslationYForHotseat: Float
+ get() {
+ val hotseatBottomSpace = taskbarHotseatDimensionsProvider.getHotseatBottomSpace()
+ val hotseatCellHeight = taskbarHotseatDimensionsProvider.getHotseatHeight()
+ val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
+ return -hotseatBottomSpace - (hotseatCellHeight - bubbleBarHeight) / 2
+ }
+
+ override fun init(
+ taskbarInsetsController: TaskbarInsetsController,
+ bubbleBarViewController: BubbleBarViewController,
+ bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
+ controllersAfterInitAction: ControllersAfterInitAction
+ ) {
+ this.taskbarInsetsController = taskbarInsetsController
+ this.bubbleBarViewController = bubbleBarViewController
+ this.controllersAfterInitAction = controllersAfterInitAction
+ bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
+ // bubble bar has only alpha property, getting it at index 0
+ bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
+ bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScale
+ }
+
+ private fun animateAfterUnlock() {
+ val animatorSet = AnimatorSet()
+ if (isBubblesShowingOnHome || isBubblesShowingOnOverview) {
+ animatorSet.playTogether(
+ bubbleBarScaleAnimator.animateToValue(1f),
+ bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
+ bubbleBarAlphaAnimator.animateToValue(1f)
+ )
+ }
+ updateTouchRegionOnAnimationEnd(animatorSet)
+ animatorSet.setDuration(BAR_STASH_DURATION).start()
+ }
+
+ override fun showBubbleBarImmediate() = showBubbleBarImmediate(bubbleBarTranslationY)
+
+ override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
+ bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
+ bubbleBarAlphaAnimator.setValue(1f)
+ bubbleBarScaleAnimator.updateValue(1f)
+ }
+
+ override fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {
+ // When the bubble bar is shown for the persistent task bar, there is no handle view, so no
+ // operation is performed.
+ }
+
+ override fun stashBubbleBar() {
+ updateExpandedState(expand = false)
+ }
+
+ override fun showBubbleBar(expandBubbles: Boolean) {
+ updateExpandedState(expandBubbles)
+ }
+
+ override fun stashBubbleBarImmediate() {
+ // When the bubble bar is shown for the persistent task bar, there is no handle view, so no
+ // operation is performed.
+ }
+
+ /** If bubble bar is visible return bubble bar height, 0 otherwise */
+ override fun getTouchableHeight() =
+ if (isBubbleBarVisible()) {
+ bubbleBarViewController.bubbleBarCollapsedHeight.toInt()
+ } else {
+ 0
+ }
+
+ override fun isBubbleBarVisible(): Boolean = bubbleBarViewController.hasBubbles()
+
+ override fun onNewBubbleAnimationInterrupted(isStashed: Boolean, bubbleBarTranslationY: Float) {
+ showBubbleBarImmediate(bubbleBarTranslationY)
+ }
+
+ override fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean =
+ bubbleBarViewController.isEventOverAnyItem(ev)
+
+ override fun getDiffBetweenHandleAndBarCenters(): Float {
+ // distance from the bottom of the screen and the bubble bar center.
+ return -bubbleBarViewController.bubbleBarCollapsedHeight / 2f
+ }
+
+ /** When the bubble bar is shown for the persistent task bar, there is no handle view. */
+ override fun getStashedHandleTranslationForNewBubbleAnimation(): Float = 0f
+
+ /** When the bubble bar is shown for the persistent task bar, there is no handle view. */
+ override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? = null
+
+ override fun updateTaskbarTouchRegion() {
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ /**
+ * When the bubble bar is shown for the persistent task bar the bar does not stash, so no
+ * operation is performed
+ */
+ override fun setHandleTranslationY(translationY: Float) {
+ // no op since does not have a handle view
+ }
+
+ private fun updateExpandedState(expand: Boolean) {
+ if (bubbleBarViewController.isHiddenForNoBubbles) {
+ // If there are no bubbles the bar is invisible, nothing to do here.
+ return
+ }
+ if (bubbleBarViewController.isExpanded != expand) {
+ bubbleBarViewController.isExpanded = expand
+ }
+ }
+
+ /** Animates bubble bar Y accordingly to the showing mode */
+ private fun animateBubbleBarY() {
+ val animator =
+ bubbleBarViewController.bubbleBarTranslationY.animateToValue(bubbleBarTranslationY)
+ updateTouchRegionOnAnimationEnd(animator)
+ animator.setDuration(BAR_TRANSLATION_DURATION)
+ animator.start()
+ }
+
+ private fun updateTouchRegionOnAnimationEnd(animator: Animator) {
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+
+ override fun onAnimationEnd(animation: Animator) {
+ controllersAfterInitAction.runAfterInit {
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+ }
+ }
+ )
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
new file mode 100644
index 0000000..23e009b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -0,0 +1,373 @@
+/*
+ * 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.stashing
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.content.res.Resources
+import android.view.MotionEvent
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.R
+import com.android.launcher3.anim.AnimatedFloat
+import com.android.launcher3.taskbar.StashedHandleViewController
+import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+
+class TransientBubbleStashController(
+ private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
+ resources: Resources
+) : BubbleStashController {
+
+ private lateinit var bubbleBarViewController: BubbleBarViewController
+ private lateinit var taskbarInsetsController: TaskbarInsetsController
+ private lateinit var controllersAfterInitAction: ControllersAfterInitAction
+
+ // stash view properties
+ private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null
+ private var stashHandleViewAlpha: MultiPropertyFactory<View>.MultiProperty? = null
+ private var stashedHeight: Int = 0
+
+ // bubble bar properties
+ private lateinit var bubbleBarAlpha: MultiPropertyFactory<View>.MultiProperty
+ private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
+ private lateinit var bubbleBarScale: AnimatedFloat
+ private val mHandleCenterFromScreenBottom =
+ resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f
+
+ private var animator: AnimatorSet? = null
+
+ override var isStashed: Boolean = false
+ @VisibleForTesting set
+
+ override var isBubblesShowingOnHome: Boolean = false
+ set(onHome) {
+ if (field == onHome) return
+ field = onHome
+ if (!bubbleBarViewController.hasBubbles()) {
+ // if there are no bubbles, there's nothing to show, so just return.
+ return
+ }
+ if (onHome) {
+ updateStashedAndExpandedState(stash = false, expand = false)
+ // When transitioning from app to home we need to animate the bubble bar
+ // here to align with hotseat center.
+ animateBubbleBarYToHotseat()
+ } else if (!bubbleBarViewController.isExpanded) {
+ updateStashedAndExpandedState(stash = true, expand = false)
+ }
+ bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
+ }
+
+ override var isBubblesShowingOnOverview: Boolean = false
+ set(onOverview) {
+ if (field == onOverview) return
+ field = onOverview
+ if (onOverview) {
+ // When transitioning to overview we need to animate the bubble bar to align with
+ // the taskbar bottom.
+ animateBubbleBarYToTaskbar()
+ } else {
+ updateStashedAndExpandedState(stash = true, expand = false)
+ }
+ bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
+ }
+
+ override var isSysuiLocked: Boolean = false
+ set(isLocked) {
+ if (field == isLocked) return
+ field = isLocked
+ if (!isLocked && bubbleBarViewController.hasBubbles()) {
+ animateAfterUnlock()
+ }
+ }
+
+ override val isTransientTaskBar: Boolean = true
+
+ override val bubbleBarTranslationYForHotseat: Float
+ get() {
+ val hotseatBottomSpace = taskbarHotseatDimensionsProvider.getHotseatBottomSpace()
+ val hotseatCellHeight = taskbarHotseatDimensionsProvider.getHotseatHeight()
+ val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
+ return -hotseatBottomSpace - (hotseatCellHeight - bubbleBarHeight) / 2
+ }
+
+ override val bubbleBarTranslationYForTaskbar: Float =
+ -taskbarHotseatDimensionsProvider.getTaskbarBottomSpace().toFloat()
+
+ /** Check if we have handle view controller */
+ override val hasHandleView: Boolean
+ get() = bubbleStashedHandleViewController != null
+
+ override fun init(
+ taskbarInsetsController: TaskbarInsetsController,
+ bubbleBarViewController: BubbleBarViewController,
+ bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
+ controllersAfterInitAction: ControllersAfterInitAction
+ ) {
+ this.taskbarInsetsController = taskbarInsetsController
+ this.bubbleBarViewController = bubbleBarViewController
+ this.bubbleStashedHandleViewController = bubbleStashedHandleViewController
+ this.controllersAfterInitAction = controllersAfterInitAction
+ bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
+ // bubble bar has only alpha property, getting it at index 0
+ bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
+ bubbleBarScale = bubbleBarViewController.bubbleBarScale
+ stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
+ stashHandleViewAlpha =
+ bubbleStashedHandleViewController
+ ?.stashedHandleAlpha
+ ?.get(StashedHandleViewController.ALPHA_INDEX_STASHED)
+ }
+
+ private fun animateAfterUnlock() {
+ val animatorSet = AnimatorSet()
+ if (isBubblesShowingOnHome || isBubblesShowingOnOverview) {
+ isStashed = false
+ animatorSet.playTogether(
+ bubbleBarScale.animateToValue(1f),
+ bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
+ bubbleBarAlpha.animateToValue(1f)
+ )
+ } else {
+ isStashed = true
+ stashHandleViewAlpha?.let { animatorSet.playTogether(it.animateToValue(1f)) }
+ }
+ animatorSet.updateTouchRegionOnAnimationEnd().setDuration(BAR_STASH_DURATION).start()
+ }
+
+ override fun showBubbleBarImmediate() {
+ showBubbleBarImmediate(bubbleBarTranslationY)
+ }
+
+ override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
+ bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
+ stashHandleViewAlpha?.value = 0f
+ this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
+ bubbleBarAlpha.setValue(1f)
+ bubbleBarScale.updateValue(1f)
+ isStashed = false
+ onIsStashedChanged()
+ }
+
+ override fun stashBubbleBarImmediate() {
+ bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
+ stashHandleViewAlpha?.value = 1f
+ this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation())
+ bubbleBarAlpha.setValue(0f)
+ bubbleBarScale.updateValue(STASHED_BAR_SCALE)
+ isStashed = true
+ onIsStashedChanged()
+ }
+
+ override fun getTouchableHeight(): Int =
+ when {
+ isStashed -> stashedHeight
+ isBubbleBarVisible() -> bubbleBarViewController.bubbleBarCollapsedHeight.toInt()
+ else -> 0
+ }
+
+ override fun isBubbleBarVisible(): Boolean = bubbleBarViewController.hasBubbles() && !isStashed
+
+ override fun onNewBubbleAnimationInterrupted(isStashed: Boolean, bubbleBarTranslationY: Float) =
+ if (isStashed) {
+ stashBubbleBarImmediate()
+ } else {
+ showBubbleBarImmediate(bubbleBarTranslationY)
+ }
+
+ /** Check if [ev] belongs to the stash handle or the bubble bar views. */
+ override fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean {
+ val isOverHandle = bubbleStashedHandleViewController?.isEventOverHandle(ev) ?: false
+ return isOverHandle || bubbleBarViewController.isEventOverAnyItem(ev)
+ }
+
+ /** Set the bubble bar stash handle location . */
+ override fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {
+ bubbleStashedHandleViewController?.setBubbleBarLocation(bubbleBarLocation)
+ }
+
+ override fun stashBubbleBar() {
+ updateStashedAndExpandedState(stash = true, expand = false)
+ }
+
+ override fun showBubbleBar(expandBubbles: Boolean) {
+ updateStashedAndExpandedState(stash = false, expandBubbles)
+ }
+
+ override fun getDiffBetweenHandleAndBarCenters(): Float {
+ // the difference between the centers of the handle and the bubble bar is the difference
+ // between their distance from the bottom of the screen.
+ val barCenter: Float = bubbleBarViewController.bubbleBarCollapsedHeight / 2f
+ return mHandleCenterFromScreenBottom - barCenter
+ }
+
+ override fun getStashedHandleTranslationForNewBubbleAnimation(): Float {
+ return -mHandleCenterFromScreenBottom
+ }
+
+ override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? {
+ return bubbleStashedHandleViewController?.physicsAnimator
+ }
+
+ override fun updateTaskbarTouchRegion() {
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ override fun setHandleTranslationY(translationY: Float) {
+ bubbleStashedHandleViewController?.setTranslationYForSwipe(translationY)
+ }
+
+ private fun getStashTranslation(): Float {
+ return (bubbleBarViewController.bubbleBarCollapsedHeight - stashedHeight) / 2f
+ }
+
+ /**
+ * Create a stash animation.
+ *
+ * @param isStashed whether it's a stash animation or an unstash animation
+ * @param duration duration of the animation
+ * @return the animation
+ */
+ @Suppress("SameParameterValue")
+ private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet {
+ val animatorSet = AnimatorSet()
+ val fullLengthAnimatorSet = AnimatorSet()
+ // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
+ val firstHalfAnimatorSet = AnimatorSet()
+ val secondHalfAnimatorSet = AnimatorSet()
+ val firstHalfDurationScale: Float
+ val secondHalfDurationScale: Float
+ val stashHandleAlphaValue: Float
+ if (isStashed) {
+ firstHalfDurationScale = 0.75f
+ secondHalfDurationScale = 0.5f
+ stashHandleAlphaValue = 1f
+ fullLengthAnimatorSet.play(
+ bubbleBarTranslationYAnimator.animateToValue(getStashTranslation())
+ )
+ firstHalfAnimatorSet.playTogether(
+ bubbleBarAlpha.animateToValue(0f),
+ bubbleBarScale.animateToValue(STASHED_BAR_SCALE)
+ )
+ } else {
+ firstHalfDurationScale = 0.5f
+ secondHalfDurationScale = 0.75f
+ stashHandleAlphaValue = 0f
+ fullLengthAnimatorSet.playTogether(
+ bubbleBarScale.animateToValue(1f),
+ bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY)
+ )
+ secondHalfAnimatorSet.playTogether(bubbleBarAlpha.animateToValue(1f))
+ }
+ stashHandleViewAlpha?.let {
+ secondHalfAnimatorSet.playTogether(it.animateToValue(stashHandleAlphaValue))
+ }
+ bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.let {
+ fullLengthAnimatorSet.play(it)
+ }
+ fullLengthAnimatorSet.setDuration(duration)
+ firstHalfAnimatorSet.setDuration((duration * firstHalfDurationScale).toLong())
+ secondHalfAnimatorSet.setDuration((duration * secondHalfDurationScale).toLong())
+ secondHalfAnimatorSet.startDelay = (duration * (1 - secondHalfDurationScale)).toLong()
+ animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet)
+ animatorSet.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ animator = null
+ controllersAfterInitAction.runAfterInit {
+ if (isStashed) {
+ bubbleBarViewController.isExpanded = false
+ }
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+ }
+ }
+ )
+ return animatorSet
+ }
+
+ private fun onIsStashedChanged() {
+ controllersAfterInitAction.runAfterInit {
+ taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ bubbleStashedHandleViewController?.onIsStashedChanged()
+ }
+ }
+
+ private fun animateBubbleBarYToHotseat() {
+ translateBubbleBarYUpdateTouchRegionOnCompletion(bubbleBarTranslationYForHotseat)
+ }
+
+ private fun animateBubbleBarYToTaskbar() {
+ translateBubbleBarYUpdateTouchRegionOnCompletion(bubbleBarTranslationYForTaskbar)
+ }
+
+ private fun translateBubbleBarYUpdateTouchRegionOnCompletion(toY: Float) {
+ bubbleBarViewController.bubbleBarTranslationY
+ .animateToValue(toY)
+ .updateTouchRegionOnAnimationEnd()
+ .setDuration(BAR_TRANSLATION_DURATION)
+ .start()
+ }
+
+ @VisibleForTesting
+ fun updateStashedAndExpandedState(stash: Boolean, expand: Boolean) {
+ if (bubbleBarViewController.isHiddenForNoBubbles) {
+ // If there are no bubbles the bar and handle are invisible, nothing to do here.
+ return
+ }
+ val isStashed = stash && !isBubblesShowingOnHome && !isBubblesShowingOnOverview
+ if (this.isStashed != isStashed) {
+ this.isStashed = isStashed
+ // notify the view controller that the stash state is about to change so that it can
+ // cancel an ongoing animation if there is one.
+ // note that this has to be called before updating mIsStashed with the new value,
+ // otherwise interrupting an ongoing animation may update it again with the wrong state
+ bubbleBarViewController.onStashStateChanging()
+ animator?.cancel()
+ animator =
+ createStashAnimator(isStashed, BAR_STASH_DURATION).apply {
+ updateTouchRegionOnAnimationEnd()
+ start()
+ }
+ }
+ if (bubbleBarViewController.isExpanded != expand) {
+ bubbleBarViewController.isExpanded = expand
+ }
+ }
+
+ private fun Animator.updateTouchRegionOnAnimationEnd(): Animator {
+ this.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ onIsStashedChanged()
+ }
+ }
+ )
+ return this
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 415a051..7d2d36d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -47,7 +47,7 @@
override val spaceNeeded: Int
get() {
- return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat())
+ return dpToPx(activityContext.taskbarSpecsEvaluator!!.taskbarIconSize.size.toFloat())
}
init {
@@ -58,15 +58,15 @@
private fun setUpIcon() {
val drawable =
resources.getDrawable(
- getAllAppsButton(activityContext.taskbarFeatureEvaluator.isTransient)
+ getAllAppsButton(activityContext.taskbarFeatureEvaluator!!.isTransient)
)
- val padding = activityContext.taskbarSpecsEvaluator.taskbarIconPadding
+ val padding = activityContext.taskbarSpecsEvaluator!!.taskbarIconPadding
allAppsButton.setIconDrawable(drawable)
- allAppsButton.setPadding(/* left= */ padding)
+ allAppsButton.setPadding(padding)
allAppsButton.setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
- // TODO(jagrutdesai) : add click listeners in future cl
+ // TODO(b/356465292) : add click listeners in future cl
addView(allAppsButton)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
new file mode 100644
index 0000000..26e71f7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.customization
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import androidx.core.view.setPadding
+import com.android.launcher3.R
+import com.android.launcher3.Utilities.dpToPx
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.views.ActivityContext
+import com.android.launcher3.views.IconButtonView
+
+/** Taskbar divider view container for customizable taskbar. */
+class TaskbarDividerContainer
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+) : LinearLayout(context, attrs), TaskbarContainer {
+
+ private val taskbarDivider: IconButtonView =
+ LayoutInflater.from(context).inflate(R.layout.taskbar_divider, this, false)
+ as IconButtonView
+ private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
+
+ override val spaceNeeded: Int
+ get() {
+ return dpToPx(activityContext.taskbarSpecsEvaluator!!.taskbarIconSize.size.toFloat())
+ }
+
+ init {
+ setUpIcon()
+ }
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ fun setUpIcon() {
+ val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
+ val padding = activityContext.taskbarSpecsEvaluator!!.taskbarIconPadding
+
+ taskbarDivider.setIconDrawable(drawable)
+ taskbarDivider.setPadding(padding)
+
+ // TODO(b/356465292):: add click listeners in future cl
+ addView(taskbarDivider)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index be6f690..b2cc369 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -64,9 +64,8 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.QUICK_SWITCH_FROM_HOME_FALLBACK;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
@@ -200,6 +199,9 @@
import com.android.systemui.unfold.dagger.UnfoldMain;
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
import com.android.systemui.unfold.updates.RotationChangeProvider;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+
+import kotlin.Unit;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -213,8 +215,6 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
-import kotlin.Unit;
-
public class QuickstepLauncher extends Launcher implements RecentsViewContainer {
private static final boolean TRACE_LAYOUTS =
SystemProperties.getBoolean("persist.debug.trace_layouts", false);
@@ -256,6 +256,10 @@
private boolean mIsPredictiveBackToHomeInProgress;
+ private boolean mCanShowAllAppsEducationView;
+
+ private boolean mIsOverlayVisible;
+
public static QuickstepLauncher getLauncher(Context context) {
return fromContext(context);
}
@@ -276,7 +280,7 @@
// TODO(b/337863494): Explore use of the same OverviewComponentObserver across launcher
OverviewComponentObserver overviewComponentObserver = new OverviewComponentObserver(
asContext(), deviceState);
- if (enableDesktopWindowingMode()) {
+ if (DesktopModeStatus.canEnterDesktopMode(this)) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
getDepthController());
@@ -296,7 +300,7 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
- if (enableDesktopWindowingMode()) {
+ if (DesktopModeStatus.canEnterDesktopMode(this)) {
mDesktopVisibilityController = new DesktopVisibilityController(this);
mDesktopVisibilityController.registerSystemUiListener();
mSplitSelectStateController.initSplitFromDesktopController(this,
@@ -493,7 +497,8 @@
(getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
boolean visible = (state == NORMAL || state == OVERVIEW)
&& (willUserBeActive || isUserActive())
- && !profile.isVerticalBarLayout();
+ && !profile.isVerticalBarLayout()
+ && !mIsOverlayVisible;
SystemUiProxy.INSTANCE.get(this)
.setLauncherKeepClearAreaHeight(visible, profile.hotseatBarSizePx);
}
@@ -503,6 +508,12 @@
}
@Override
+ public void onOverlayVisibilityChanged(boolean visible) {
+ super.onOverlayVisibilityChanged(visible);
+ mIsOverlayVisible = visible;
+ }
+
+ @Override
public void bindExtraContainerItems(FixedContainerItems item) {
if (item.containerId == Favorites.CONTAINER_PREDICTION) {
mAllAppsPredictions = item;
@@ -513,7 +524,7 @@
} else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
mHotseatPredictionController.setPredictedItems(item);
} else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
- getPopupDataProvider().setRecommendedWidgets(item.items);
+ getWidgetPickerDataProvider().setWidgetRecommendations(item.items);
}
}
@@ -1004,7 +1015,7 @@
@Override
public void setResumed() {
- if (!enableDesktopWindowingWallpaperActivity()
+ if (!WALLPAPER_ACTIVITY.isEnabled(this)
&& mDesktopVisibilityController != null
&& mDesktopVisibilityController.areDesktopTasksVisible()
&& !mDesktopVisibilityController.isRecentsGestureInProgress()) {
@@ -1494,4 +1505,12 @@
public boolean isRecentsViewVisible() {
return getStateManager().getState().isRecentsViewVisible;
}
+
+ public boolean isCanShowAllAppsEducationView() {
+ return mCanShowAllAppsEducationView;
+ }
+
+ public void setCanShowAllAppsEducationView(boolean canShowAllAppsEducationView) {
+ mCanShowAllAppsEducationView = canShowAllAppsEducationView;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 3325009..d1aa472 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -39,7 +39,6 @@
import android.view.ViewConfiguration;
import com.android.internal.jank.Cuj;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -88,13 +87,17 @@
// Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
private ObjectAnimator mNormalToHintOverviewScrimAnimator;
+ private final QuickstepLauncher mLauncher;
+ private boolean mIsTrackpadSwipe;
+
/**
* @param cancelSplitRunnable Called when split placeholder view needs to be cancelled.
* Animation should be added to the provided AnimatorSet
*/
- public NoButtonNavbarToOverviewTouchController(Launcher l,
+ public NoButtonNavbarToOverviewTouchController(QuickstepLauncher l,
BiConsumer<AnimatorSet, Long> cancelSplitRunnable) {
super(l);
+ mLauncher = l;
mRecentsView = l.getOverviewPanel();
mMotionPauseDetector = new MotionPauseDetector(l);
mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
@@ -104,7 +107,9 @@
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
- if (!isTrackpadMotionEvent(ev) && DisplayController.getNavigationMode(mLauncher)
+ mIsTrackpadSwipe = isTrackpadMotionEvent(ev);
+ mLauncher.setCanShowAllAppsEducationView(!mIsTrackpadSwipe);
+ if (!mIsTrackpadSwipe && DisplayController.getNavigationMode(mLauncher)
== THREE_BUTTONS) {
return false;
}
@@ -148,6 +153,7 @@
super.onDragStart(start, startDisplacement);
mMotionPauseDetector.clear();
+ mMotionPauseDetector.setIsTrackpadGesture(mIsTrackpadSwipe);
if (handlingOverviewAnim()) {
InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
@@ -191,6 +197,7 @@
}
mMotionPauseDetector.clear();
+ mIsTrackpadSwipe = false;
mNormalToHintOverviewScrimAnimator = null;
if (mLauncher.isInState(OVERVIEW)) {
// Normally we would cleanup the state based on mCurrentAnimation, but since we stop
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index ab277b6..0da7b2d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -121,6 +121,7 @@
private AnimatorPlaybackController mNonOverviewAnim;
private AnimatorPlaybackController mXOverviewAnim;
private AnimatedFloat mYOverviewAnim;
+ private boolean mIsTrackpadSwipe;
public NoButtonQuickSwitchTouchController(QuickstepLauncher launcher) {
mLauncher = launcher;
@@ -177,7 +178,8 @@
return false;
}
if (isTrackpadMultiFingerSwipe(ev)) {
- return isTrackpadFourFingerSwipe(ev);
+ mIsTrackpadSwipe = isTrackpadFourFingerSwipe(ev);
+ return mIsTrackpadSwipe;
}
return true;
}
@@ -185,6 +187,7 @@
@Override
public void onDragStart(boolean start) {
mMotionPauseDetector.clear();
+ mMotionPauseDetector.setIsTrackpadGesture(mIsTrackpadSwipe);
if (start) {
InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 20eaddc..3c9bd0f 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -148,10 +148,13 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.window.flags.Flags;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -161,8 +164,6 @@
import java.util.OptionalInt;
import java.util.function.Consumer;
-import kotlin.Unit;
-
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
@@ -952,7 +953,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- if (targets.hasDesktopTasks()) {
+ if (targets.hasDesktopTasks(mContext)) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
@@ -1272,9 +1273,9 @@
TaskView currentPageTaskView = mRecentsView != null
? mRecentsView.getCurrentPageTaskView() : null;
- if (Flags.enableDesktopWindowingMode()
- && !(Flags.enableDesktopWindowingWallpaperActivity()
- && Flags.enableDesktopWindowingQuickSwitch())) {
+ if (DesktopModeStatus.canEnterDesktopMode(mContext)
+ && !(DesktopModeFlags.WALLPAPER_ACTIVITY.isEnabled(mContext)
+ && DesktopModeFlags.QUICK_SWITCH.isEnabled(mContext))) {
if ((nextPageTaskView instanceof DesktopTaskView
|| currentPageTaskView instanceof DesktopTaskView)
&& endTarget == NEW_TASK) {
@@ -1445,9 +1446,9 @@
setClampScrollOffset(false);
};
- if (Flags.enableDesktopWindowingMode()
- && !(Flags.enableDesktopWindowingWallpaperActivity()
- && Flags.enableDesktopWindowingQuickSwitch())) {
+ if (DesktopModeStatus.canEnterDesktopMode(mContext)
+ && !(DesktopModeFlags.WALLPAPER_ACTIVITY.isEnabled(mContext)
+ && DesktopModeFlags.QUICK_SWITCH.isEnabled(mContext))) {
if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
&& !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
@@ -2099,7 +2100,6 @@
// If there are no targets, then we don't need to capture anything
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
} else {
- boolean finishTransitionPosted = false;
// If we already have cached screenshot(s) from running tasks, skip update
boolean shouldUpdate = false;
int[] runningTaskIds = mGestureState.getRunningTaskIds(mIsSwipeForSplit);
@@ -2123,45 +2123,32 @@
}
MAIN_EXECUTOR.execute(() -> {
- if (!updateThumbnail(false /* refreshView */)) {
- setScreenshotCapturedState();
- }
+ updateThumbnail();
+ setScreenshotCapturedState();
});
});
return;
}
- finishTransitionPosted = updateThumbnail(false /* refreshView */);
+ updateThumbnail();
}
- if (!finishTransitionPosted) {
- setScreenshotCapturedState();
- }
+ setScreenshotCapturedState();
}
}
// Returns whether finish transition was posted.
- private boolean updateThumbnail(boolean refreshView) {
+ private void updateThumbnail() {
if (mGestureState.getEndTarget() == HOME
|| mGestureState.getEndTarget() == NEW_TASK
|| mGestureState.getEndTarget() == ALL_APPS
|| mRecentsView == null) {
// Capture the screenshot before finishing the transition to home or quickswitching to
// ensure it's taken in the correct orientation, but no need to update the thumbnail.
- return false;
+ return;
}
- boolean finishTransitionPosted = false;
- TaskView updatedTaskView = mRecentsView.updateThumbnail(mTaskSnapshotCache, refreshView);
- if (updatedTaskView != null && refreshView && !mCanceled) {
- // Defer finishing the animation until the next launcher frame with the
- // new thumbnail
- finishTransitionPosted = ViewUtils.postFrameDrawn(updatedTaskView,
- () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
- this::isCanceled);
- }
-
- return finishTransitionPosted;
+ mRecentsView.updateThumbnail(mTaskSnapshotCache);
}
private void setScreenshotCapturedState() {
@@ -2293,9 +2280,9 @@
mRecentsAnimationController, mRecentsAnimationTargets);
});
- if (Flags.enableDesktopWindowingMode()
- && !(Flags.enableDesktopWindowingWallpaperActivity()
- && Flags.enableDesktopWindowingQuickSwitch())) {
+ if (DesktopModeStatus.canEnterDesktopMode(mContext)
+ && !(DesktopModeFlags.WALLPAPER_ACTIVITY.isEnabled(mContext)
+ && DesktopModeFlags.QUICK_SWITCH.isEnabled(mContext))) {
if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
|| mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
mRecentsViewScrollLinked = false;
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 00cd60b..293944d 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -31,7 +31,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.graphics.Color;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
@@ -133,7 +132,7 @@
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
TaskbarUIController controller = getTaskbarController();
boolean isEventOverBubbleBarStashHandle =
- controller != null && controller.isEventOverBubbleBarStashHandle(ev);
+ controller != null && controller.isEventOverBubbleBarViews(ev);
return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
|| isTrackpadMultiFingerSwipe(ev) || isEventOverBubbleBarStashHandle;
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 4989831..e66da52 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,13 +20,13 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.os.Process;
import android.os.RemoteException;
import android.util.SparseBooleanArray;
@@ -40,6 +40,7 @@
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.desktopmode.DesktopModeStatus;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import java.io.PrintWriter;
@@ -58,6 +59,7 @@
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
+ private final Context mContext;
private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
@@ -76,8 +78,10 @@
// Tasks are stored in order of least recently launched to most recently launched.
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
- public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keyguardManager,
- SystemUiProxy sysUiProxy, TopTaskTracker topTaskTracker) {
+ public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
+ KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
+ TopTaskTracker topTaskTracker) {
+ mContext = context;
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
@@ -325,9 +329,9 @@
int numVisibleTasks = 0;
for (GroupedRecentTaskInfo rawTask : rawTasks) {
if (rawTask.getType() == TYPE_FREEFORM) {
- // TYPE_FREEFORM tasks is only created when enableDesktopWindowingMode() is true,
+ // TYPE_FREEFORM tasks is only created when desktop mode can be entered,
// leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
- if (enableDesktopWindowingMode()) {
+ if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
GroupTask desktopTask = createDesktopTask(rawTask);
if (desktopTask != null) {
allTasks.add(desktopTask);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 18461a6..6d5cb4b 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -27,7 +27,6 @@
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -57,6 +56,7 @@
import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.LauncherAnimationRunner.AnimationResult;
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
+import com.android.launcher3.LauncherRootView;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -87,6 +87,7 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskView;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -139,15 +140,15 @@
null /* depthController */, getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
+ // Setup root and child views
inflateRootView(R.layout.fallback_recents_activity);
- setContentView(getRootView());
- mDragLayer = findViewById(R.id.drag_layer);
- mScrimView = findViewById(R.id.scrim_view);
- mFallbackRecentsView = findViewById(R.id.overview_panel);
- mActionsView = findViewById(R.id.overview_actions_view);
- getRootView().getSysUiScrim().getSysUIProgress().updateValue(0);
- mDragLayer.recreateControllers();
- if (enableDesktopWindowingMode()) {
+ LauncherRootView rootView = getRootView();
+ mDragLayer = rootView.findViewById(R.id.drag_layer);
+ mScrimView = rootView.findViewById(R.id.scrim_view);
+ mFallbackRecentsView = rootView.findViewById(R.id.overview_panel);
+ mActionsView = rootView.findViewById(R.id.overview_actions_view);
+
+ if (DesktopModeStatus.canEnterDesktopMode(this)) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
null /* depthController */
@@ -156,6 +157,10 @@
mFallbackRecentsView.init(mActionsView, mSplitSelectStateController,
mDesktopRecentsTransitionController);
+ setContentView(rootView);
+ rootView.getSysUiScrim().getSysUIProgress().updateValue(0);
+ mDragLayer.recreateControllers();
+
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index da7a98f..32e2389 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -128,15 +128,11 @@
mController::finishAnimationToApp);
} else {
RemoteAnimationTarget[] nonAppTargets;
- if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
- } else {
- final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
- final ArrayList<RemoteAnimationTarget> nonApps = new ArrayList<>();
- classifyTargets(appTargets, apps, nonApps);
- appTargets = apps.toArray(new RemoteAnimationTarget[apps.size()]);
- nonAppTargets = nonApps.toArray(new RemoteAnimationTarget[nonApps.size()]);
- }
+ final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
+ final ArrayList<RemoteAnimationTarget> nonApps = new ArrayList<>();
+ classifyTargets(appTargets, apps, nonApps);
+ appTargets = apps.toArray(new RemoteAnimationTarget[apps.size()]);
+ nonAppTargets = nonApps.toArray(new RemoteAnimationTarget[nonApps.size()]);
if (nonAppTargets == null) {
nonAppTargets = new RemoteAnimationTarget[0];
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 82bb453..cf7e499 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -18,13 +18,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
-
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.RemoteAnimationTarget;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+
import java.io.PrintWriter;
/**
@@ -54,8 +55,8 @@
*
* @return {@code true} if at least one target app is a desktop task
*/
- public boolean hasDesktopTasks() {
- if (!enableDesktopWindowingMode()) {
+ public boolean hasDesktopTasks(Context context) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return false;
}
for (RemoteAnimationTarget target : apps) {
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index f2b6005..db03dac 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -90,7 +90,9 @@
private RecentsModel(Context context, IconProvider iconProvider) {
this(context,
- new RecentTasksList(MAIN_EXECUTOR,
+ new RecentTasksList(
+ context,
+ MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
SystemUiProxy.INSTANCE.get(context),
TopTaskTracker.INSTANCE.get(context)),
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 3f73959..0c2f29b 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -23,8 +23,6 @@
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
-import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -96,6 +94,8 @@
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
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.splitscreen.ISplitScreen;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
import com.android.wm.shell.splitscreen.ISplitSelectListener;
@@ -1000,77 +1000,6 @@
}
}
- /**
- * Start multiple tasks in split-screen simultaneously.
- */
- public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
- Bundle options2, @StagePosition int splitPosition,
- @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
- splitPosition, snapPosition, adapter, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage(
- "startTasksWithLegacyTransition", "RemoteException"), e);
- }
- }
- }
-
- public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
- Bundle options1, int taskId, Bundle options2, @StagePosition int splitPosition,
- @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1,
- options1, taskId, options2, splitPosition, snapPosition, adapter,
- instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage(
- "startIntentAndTaskWithLegacyTransition", "RemoteException"), e);
- }
- }
- }
-
- public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
- int taskId, Bundle options2, @StagePosition int splitPosition,
- @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
- taskId, options2, splitPosition, snapPosition, adapter, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage(
- "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e);
- }
- }
- }
-
- /**
- * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
- * non-null shortcut info means to start the app as a shortcut.
- */
- public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
- @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
- PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
- @Nullable Bundle options2, @StagePosition int sidePosition,
- @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
- if (mSystemUiProxy != null) {
- try {
- mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1,
- shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2,
- sidePosition, snapPosition, adapter, instanceId);
- } catch (RemoteException e) {
- Log.w(TAG, splitFailureMessage(
- "startIntentsWithLegacyTransition", "RemoteException"), e);
- }
- }
- }
-
public void startShortcut(String packageName, String shortcutId, int position,
Bundle options, UserHandle user, InstanceId instanceId) {
if (mSplitScreen != null) {
@@ -1105,36 +1034,6 @@
}
}
- /**
- * Call this when going to recents so that shell can set-up and provide appropriate leashes
- * for animation (eg. DividerBar).
- *
- * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
- */
- @Nullable
- public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
- if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) {
- try {
- return mSplitScreen.onGoingToRecentsLegacy(apps);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onGoingToRecentsLegacy");
- }
- }
- return null;
- }
-
- @Nullable
- public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
- if (mSplitScreen != null) {
- try {
- return mSplitScreen.onStartingSplitLegacy(apps);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onStartingSplitLegacy");
- }
- }
- return null;
- }
-
//
// One handed
//
@@ -1443,8 +1342,8 @@
}
private boolean shouldEnableRunningTasksForDesktopMode() {
- // TODO(b/335401172): unify DesktopMode checks in Launcher
- return enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps();
+ return DesktopModeStatus.canEnterDesktopMode(mContext)
+ && DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(mContext);
}
private boolean handleMessageAsync(Message msg) {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 723aa03..f414399 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -264,12 +264,7 @@
}
}
- RemoteAnimationTarget[] nonAppTargets = ENABLE_SHELL_TRANSITIONS
- ? null : getSystemUiProxy().onStartingSplitLegacy(
- appearedTaskTargets);
- if (nonAppTargets == null) {
- nonAppTargets = new RemoteAnimationTarget[0];
- }
+ RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
if ((containerInterface.isInLiveTileMode()
|| mLastGestureState.getEndTarget() == RECENTS
|| isNonRecentsStartedTasksAppeared(appearedTaskTargets))
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 6ed05c8..3cf0542 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -28,7 +28,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
-import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -48,7 +47,6 @@
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -63,10 +61,6 @@
public class TopTaskTracker extends ISplitScreenListener.Stub
implements TaskStackChangeListener, SafeCloseable {
- private static final String TAG = "TopTaskTracker";
-
- private static final boolean DEBUG = true;
-
public static MainThreadInitializedObject<TopTaskTracker> INSTANCE =
new MainThreadInitializedObject<>(TopTaskTracker::new);
@@ -98,19 +92,10 @@
@Override
public void onTaskRemoved(int taskId) {
mOrderedTaskList.removeIf(rto -> rto.taskId == taskId);
- if (DEBUG) {
- Log.i(TAG, "onTaskRemoved: taskId=" + taskId);
- }
}
@Override
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
- if (!mOrderedTaskList.isEmpty()
- && mOrderedTaskList.getFirst().taskId != taskInfo.taskId
- && DEBUG) {
- Log.i(TAG, "onTaskMovedToFront: (moved taskInfo to front) taskId=" + taskInfo.taskId
- + ", baseIntent=" + taskInfo.baseIntent);
- }
mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId);
mOrderedTaskList.addFirst(taskInfo);
@@ -121,11 +106,6 @@
final RunningTaskInfo topTaskOnHomeDisplay = mOrderedTaskList.stream()
.filter(rto -> rto.displayId == DEFAULT_DISPLAY).findFirst().orElse(null);
if (topTaskOnHomeDisplay != null) {
- if (DEBUG) {
- Log.i(TAG, "onTaskMovedToFront: (removing top task on home display) taskId="
- + topTaskOnHomeDisplay.taskId
- + ", baseIntent=" + topTaskOnHomeDisplay.baseIntent);
- }
mOrderedTaskList.removeIf(rto -> rto.taskId == topTaskOnHomeDisplay.taskId);
mOrderedTaskList.addFirst(topTaskOnHomeDisplay);
}
@@ -139,10 +119,6 @@
if (info.taskId != taskInfo.taskId
&& info.taskId != mMainStagePosition.taskId
&& info.taskId != mSideStagePosition.taskId) {
- if (DEBUG) {
- Log.i(TAG, "onTaskMovedToFront: (removing task list overflow) taskId="
- + taskInfo.taskId + ", baseIntent=" + taskInfo.baseIntent);
- }
itr.remove();
return;
}
@@ -152,9 +128,6 @@
@Override
public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
- if (DEBUG) {
- Log.i(TAG, "onStagePositionChanged: stage=" + stage + ", position=" + position);
- }
if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
mMainStagePosition.stagePosition = position;
} else {
@@ -164,10 +137,6 @@
@Override
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
- if (DEBUG) {
- Log.i(TAG, "onTaskStageChanged: taskId=" + taskId
- + ", stage=" + stage + ", visible=" + visible);
- }
// If a task is not visible anymore or has been moved to undefined, stop tracking it.
if (!visible || stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
if (mMainStagePosition.taskId == taskId) {
@@ -187,18 +156,11 @@
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- if (DEBUG) {
- Log.i(TAG, "onActivityPinned: packageName=" + packageName
- + ", userId=" + userId + ", stackId=" + stackId);
- }
mPinnedTaskId = taskId;
}
@Override
public void onActivityUnpinned() {
- if (DEBUG) {
- Log.i(TAG, "onActivityUnpinned");
- }
mPinnedTaskId = INVALID_TASK_ID;
}
@@ -250,21 +212,6 @@
return new CachedTaskInfo(tasks);
}
- public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "TopTaskTracker:");
-
- writer.println(prefix + "\tmOrderedTaskList=[");
- for (RunningTaskInfo taskInfo : mOrderedTaskList) {
- writer.println(prefix + "\t\t(taskId=" + taskInfo.taskId
- + "; baseIntent=" + taskInfo.baseIntent
- + "; isRunning=" + taskInfo.isRunning + ")");
- }
- writer.println(prefix + "\t]");
- writer.println(prefix + "\tmMainStagePosition=" + mMainStagePosition);
- writer.println(prefix + "\tmSideStagePosition=" + mSideStagePosition);
- writer.println(prefix + "\tmPinnedTaskId=" + mPinnedTaskId);
- }
-
/**
* Class to provide information about a task which can be safely cached and do not change
* during the lifecycle of the task.
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 2896979..5c940a3 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1610,7 +1610,6 @@
pw.println("\tmConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
RecentsModel.INSTANCE.get(this).dump("", pw);
- TopTaskTracker.INSTANCE.get(this).dump("", pw);
if (mTaskAnimationManager != null) {
mTaskAnimationManager.dump("", pw);
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index 186c453..f4d3695 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -27,6 +27,8 @@
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.DisplayController;
@@ -48,7 +50,7 @@
private static final boolean DEBUG_NAV_HANDLE = Utilities.isPropertyEnabled(
NAV_HANDLE_LONG_PRESS);
- private final NavHandleLongPressHandler mNavHandleLongPressHandler;
+ private NavHandleLongPressHandler mNavHandleLongPressHandler;
private final float mNavHandleWidth;
private final float mScreenWidth;
@@ -265,4 +267,9 @@
protected String getDelegatorName() {
return "NavHandleLongPressInputConsumer";
}
+
+ @VisibleForTesting
+ void setNavHandleLongPressHandler(NavHandleLongPressHandler navHandleLongPressHandler) {
+ mNavHandleLongPressHandler = navHandleLongPressHandler;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 13b6447..69d3bc9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -409,6 +409,7 @@
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
+ mMotionPauseDetector.setIsTrackpadGesture(mGestureState.isTrackpadGesture());
mInteractionHandler.initWhenReady(
"OtherActivityInputConsumer.startTouchTrackingForWindowAnimation");
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 95295b0..9a99d4a 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -321,8 +321,9 @@
if (controllers == null) {
return false;
}
- if (controllers.bubbleStashController.isStashed()) {
- return controllers.bubbleStashedHandleViewController.containsX((int) x);
+ 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;
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
index 9c4248c..4f7a541 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
@@ -41,4 +41,10 @@
* 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
new file mode 100644
index 0000000..feed2fd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfile.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
+
+/**
+ * Container to hold [com.android.launcher3.DeviceProfile] related to Recents.
+ *
+ * @property isLargeScreen whether the current device posture has a large screen
+ */
+data class RecentsDeviceProfile(
+ val isLargeScreen: Boolean,
+ val widthPx: Int,
+ val heightPx: Int,
+)
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepository.kt
index adf904c..13cf56d 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepository.kt
@@ -16,21 +16,6 @@
package com.android.quickstep.recents.data
-import com.android.quickstep.views.RecentsViewContainer
-
-/**
- * Repository for shrink down version of [com.android.launcher3.DeviceProfile] that only contains
- * data related to Recents.
- */
-class RecentsDeviceProfileRepository(private val container: RecentsViewContainer) {
-
- fun getRecentsDeviceProfile() =
- with(container.deviceProfile) { RecentsDeviceProfile(isLargeScreen = isTablet) }
-
- /**
- * Container to hold [com.android.launcher3.DeviceProfile] related to Recents.
- *
- * @property isLargeScreen whether the current device posture has a large screen
- */
- data class RecentsDeviceProfile(val isLargeScreen: Boolean)
+interface RecentsDeviceProfileRepository {
+ fun getRecentsDeviceProfile(): RecentsDeviceProfile
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
new file mode 100644
index 0000000..ce39ff1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImpl.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.recents.data
+
+import com.android.quickstep.views.RecentsViewContainer
+
+/**
+ * Repository for shrink down version of [com.android.launcher3.DeviceProfile] that only contains
+ * data related to Recents.
+ */
+class RecentsDeviceProfileRepositoryImpl(private val container: RecentsViewContainer) :
+ RecentsDeviceProfileRepository {
+
+ override fun getRecentsDeviceProfile() =
+ with(container.deviceProfile) {
+ RecentsDeviceProfile(isLargeScreen = isTablet, widthPx = widthPx, heightPx = heightPx)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsRotationState.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsRotationState.kt
new file mode 100644
index 0000000..2c2a744
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsRotationState.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.view.Surface
+
+/**
+ * Container to hold orientation/rotation related information related to Recents.
+ *
+ * @property activityRotation rotation of the activity hosting RecentsView
+ */
+data class RecentsRotationState(
+ @Surface.Rotation val activityRotation: Int,
+ @Surface.Rotation val orientationHandlerRotation: Int,
+)
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepository.kt
index 6ead704..ed074d2 100644
--- a/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepository.kt
@@ -16,20 +16,6 @@
package com.android.quickstep.recents.data
-import com.android.quickstep.util.RecentsOrientedState
-
-/**
- * Repository for [RecentsRotationState] which holds orientation/rotation related information
- * related to Recents
- */
-class RecentsRotationStateRepository(private val state: RecentsOrientedState) {
- fun getRecentsRotationState() =
- with(state) { RecentsRotationState(activityRotation = recentsActivityRotation) }
-
- /**
- * Container to hold orientation/rotation related information related to Recents.
- *
- * @property activityRotation rotation of the activity hosting RecentsView
- */
- data class RecentsRotationState(val activityRotation: Int)
+interface RecentsRotationStateRepository {
+ fun getRecentsRotationState(): RecentsRotationState
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryImpl.kt b/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryImpl.kt
new file mode 100644
index 0000000..8417b06
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryImpl.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.RecentsOrientedState
+
+/**
+ * Repository for [RecentsRotationState] which holds orientation/rotation related information
+ * related to Recents
+ */
+class RecentsRotationStateRepositoryImpl(private val state: RecentsOrientedState) :
+ RecentsRotationStateRepository {
+ override fun getRecentsRotationState() =
+ with(state) {
+ RecentsRotationState(
+ activityRotation = recentsActivityRotation,
+ orientationHandlerRotation = orientationHandler.rotation
+ )
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index f73db5a..6acc940 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -45,15 +45,18 @@
private val _taskData =
groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } }
private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
+ private val thumbnailOverride = MutableStateFlow(mapOf<Int, ThumbnailData>())
private val taskData: Flow<List<Task>> =
- combine(_taskData, getThumbnailQueryResults(), getIconQueryResults()) {
+ combine(_taskData, getThumbnailQueryResults(), getIconQueryResults(), thumbnailOverride) {
tasks,
thumbnailQueryResults,
- iconQueryResults ->
+ iconQueryResults,
+ thumbnailOverride ->
tasks.forEach { task ->
- // Add retrieved thumbnails + remove unnecessary thumbnails
- task.thumbnail = thumbnailQueryResults[task.key.id]
+ // Add retrieved thumbnails + remove unnecessary thumbnails (e.g. invisible)
+ task.thumbnail =
+ thumbnailOverride[task.key.id] ?: thumbnailQueryResults[task.key.id]
// TODO(b/352331675) don't load icons for DesktopTaskView
// Add retrieved icons + remove unnecessary icons
@@ -79,6 +82,15 @@
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 */
@@ -130,9 +142,15 @@
icon,
contentDescription,
title ->
- continuation.resume(
- TaskIconQueryResponse(icon, contentDescription, title)
- )
+ icon.constantState?.let {
+ continuation.resume(
+ TaskIconQueryResponse(
+ it.newDrawable().mutate(),
+ contentDescription,
+ title
+ )
+ )
+ }
}
continuation.invokeOnCancellation { cancellableTask?.cancel() }
}
@@ -157,7 +175,7 @@
}
}
-private data class TaskIconQueryResponse(
+data class TaskIconQueryResponse(
val icon: Drawable,
val contentDescription: String,
val title: String
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 3a6d5c0..eba7688 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -26,6 +26,9 @@
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
import com.android.quickstep.task.viewmodel.TaskOverlayViewModel
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
@@ -33,7 +36,6 @@
import com.android.quickstep.task.viewmodel.TaskViewModel
import com.android.quickstep.views.TaskViewType
import com.android.systemui.shared.recents.model.Task
-import java.util.logging.Level
internal typealias RecentsScopeId = String
@@ -145,13 +147,16 @@
TaskViewData(taskViewType)
}
TaskContainerData::class.java -> TaskContainerData()
+ TaskThumbnailViewData::class.java -> TaskThumbnailViewData()
TaskThumbnailViewModel::class.java ->
TaskThumbnailViewModel(
recentsViewData = inject(),
taskViewData = inject(scopeId, extras),
- taskContainerData = inject(),
+ taskContainerData = inject(scopeId),
getThumbnailPositionUseCase = inject(),
- tasksRepository = inject()
+ tasksRepository = inject(),
+ splashAlphaUseCase = inject(scopeId),
+ getSplashSizeUseCase = inject(scopeId),
)
TaskOverlayViewModel::class.java -> {
val task = extras["Task"] as Task
@@ -171,6 +176,20 @@
rotationStateRepository = inject(),
tasksRepository = inject()
)
+ SplashAlphaUseCase::class.java ->
+ SplashAlphaUseCase(
+ recentsViewData = inject(),
+ taskContainerData = inject(scopeId),
+ taskThumbnailViewData = inject(scopeId),
+ 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/RecentsViewData.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
index fdb62df..87446b0 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
@@ -31,4 +31,15 @@
// The settled set of visible taskIds that is updated after RecentsView scroll settles.
val settledFullyVisibleTaskIds = MutableStateFlow(emptySet<Int>())
+
+ // Color tint on foreground scrim
+ val tintAmount = MutableStateFlow(0f)
+
+ val thumbnailSplashProgress = MutableStateFlow(0f)
+
+ // A list of taskIds that are associated with a RecentsAnimationController. */
+ val runningTaskIds = MutableStateFlow(emptySet<Int>())
+
+ // Whether we should use static screenshot instead of live tile for taskIds in [runningTaskIds]
+ val runningTaskShowScreenshot = MutableStateFlow(false)
}
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index 8b03a84..54e34a0 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -17,6 +17,10 @@
package com.android.quickstep.recents.viewmodel
import com.android.quickstep.recents.data.RecentTasksRepository
+import com.android.systemui.shared.recents.model.ThumbnailData
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
class RecentsViewModel(
private val recentsTasksRepository: RecentTasksRepository,
@@ -45,4 +49,43 @@
fun setOverlayEnabled(isOverlayEnabled: Boolean) {
recentsViewData.overlayEnabled.value = isOverlayEnabled
}
+
+ fun setTintAmount(tintAmount: Float) {
+ recentsViewData.tintAmount.value = tintAmount
+ }
+
+ fun updateThumbnailSplashProgress(taskThumbnailSplashAlpha: Float) {
+ recentsViewData.thumbnailSplashProgress.value = taskThumbnailSplashAlpha
+ }
+
+ fun addOrUpdateThumbnailOverride(thumbnailOverride: Map<Int, ThumbnailData>) {
+ recentsTasksRepository.addOrUpdateThumbnailOverride(thumbnailOverride)
+ }
+
+ suspend fun waitForThumbnailsToUpdate(updatedThumbnails: Map<Int, ThumbnailData>) {
+ combine(
+ updatedThumbnails.map {
+ recentsTasksRepository.getThumbnailById(it.key).filter { thumbnailData ->
+ thumbnailData == it.value
+ }
+ }
+ ) {}
+ .first()
+ }
+
+ suspend fun waitForRunningTaskShowScreenshotToUpdate() {
+ recentsViewData.runningTaskShowScreenshot.filter { it }.first()
+ }
+
+ fun onReset() {
+ updateVisibleTasks(emptyList())
+ }
+
+ fun updateRunningTask(taskIds: Set<Int>) {
+ recentsViewData.runningTaskIds.value = taskIds
+ }
+
+ fun setRunningTaskShowScreenshot(showScreenshot: Boolean) {
+ recentsViewData.runningTaskShowScreenshot.value = showScreenshot
+ }
}
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/TaskContainerViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/TaskContainerViewModel.kt
index 8b8bc3e..168c1e0 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/TaskContainerViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/TaskContainerViewModel.kt
@@ -19,13 +19,20 @@
import android.graphics.Bitmap
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
import com.android.quickstep.recents.usecase.SysUiStatusNavFlagsUseCase
+import com.android.quickstep.task.thumbnail.SplashAlphaUseCase
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.runBlocking
class TaskContainerViewModel(
private val sysUiStatusNavFlagsUseCase: SysUiStatusNavFlagsUseCase,
- private val getThumbnailUseCase: GetThumbnailUseCase
+ private val getThumbnailUseCase: GetThumbnailUseCase,
+ private val splashAlphaUseCase: SplashAlphaUseCase,
) {
fun getThumbnail(taskId: Int): Bitmap? = getThumbnailUseCase.run(taskId)
fun getSysUiStatusNavFlags(taskId: Int) =
sysUiStatusNavFlagsUseCase.getSysUiStatusNavFlags(taskId)
+
+ fun shouldShowThumbnailSplash(taskId: Int): Boolean =
+ (runBlocking { splashAlphaUseCase.execute(taskId).firstOrNull() } ?: 0f) > 0f
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCase.kt b/quickstep/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCase.kt
new file mode 100644
index 0000000..145957a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCase.kt
@@ -0,0 +1,46 @@
+/*
+ * 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/SplashAlphaUseCase.kt b/quickstep/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCase.kt
new file mode 100644
index 0000000..7673c71
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCase.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.quickstep.task.thumbnail
+
+import android.graphics.Bitmap
+import android.view.Surface
+import com.android.quickstep.recents.data.RecentTasksRepository
+import com.android.quickstep.recents.data.RecentsRotationStateRepository
+import com.android.quickstep.recents.viewmodel.RecentsViewData
+import com.android.quickstep.task.viewmodel.TaskContainerData
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
+import com.android.systemui.shared.recents.utilities.Utilities
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+
+class SplashAlphaUseCase(
+ private val recentsViewData: RecentsViewData,
+ private val taskContainerData: TaskContainerData,
+ private val taskThumbnailViewData: TaskThumbnailViewData,
+ private val tasksRepository: RecentTasksRepository,
+ private val rotationStateRepository: RecentsRotationStateRepository,
+) {
+ fun execute(taskId: Int): Flow<Float> =
+ combine(
+ taskThumbnailViewData.width,
+ taskThumbnailViewData.height,
+ tasksRepository.getThumbnailById(taskId),
+ taskContainerData.thumbnailSplashProgress,
+ recentsViewData.thumbnailSplashProgress
+ ) { width, height, thumbnailData, taskSplashProgress, globalSplashProgress ->
+ val thumbnail = thumbnailData?.thumbnail
+ when {
+ thumbnail == null -> 0f
+ taskSplashProgress > 0f -> taskSplashProgress
+ globalSplashProgress > 0f &&
+ isInaccurateThumbnail(thumbnail, thumbnailData.rotation, width, height) ->
+ globalSplashProgress
+ else -> 0f
+ }
+ }
+ .distinctUntilChanged()
+
+ private fun isInaccurateThumbnail(
+ thumbnail: Bitmap,
+ thumbnailRotation: Int,
+ width: Int,
+ height: Int
+ ): Boolean {
+ return isThumbnailAspectRatioDifferentFromThumbnailData(thumbnail, width, height) ||
+ isThumbnailRotationDifferentFromTask(thumbnailRotation)
+ }
+
+ private fun isThumbnailAspectRatioDifferentFromThumbnailData(
+ thumbnail: Bitmap,
+ viewWidth: Int,
+ viewHeight: Int
+ ): Boolean {
+ val viewAspect: Float = viewWidth / viewHeight.toFloat()
+ val thumbnailAspect: Float = thumbnail.width / thumbnail.height.toFloat()
+ return Utilities.isRelativePercentDifferenceGreaterThan(
+ viewAspect,
+ thumbnailAspect,
+ PreviewPositionHelper.MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT
+ )
+ }
+
+ private fun isThumbnailRotationDifferentFromTask(thumbnailRotation: Int): Boolean {
+ val rotationState = rotationStateRepository.getRecentsRotationState()
+ return if (rotationState.orientationHandlerRotation == Surface.ROTATION_0) {
+ (rotationState.activityRotation - thumbnailRotation) % 2 != 0
+ } else {
+ rotationState.orientationHandlerRotation != thumbnailRotation
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index 3b3a811..a6be9f6 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -17,6 +17,9 @@
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
sealed class TaskThumbnailUiState {
@@ -26,8 +29,19 @@
data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
- data class Snapshot(val bitmap: Bitmap, @ColorInt val backgroundColor: Int) :
- TaskThumbnailUiState()
-}
+ data class SnapshotSplash(
+ val snapshot: Snapshot,
+ val splash: Splash,
+ ) : TaskThumbnailUiState()
-data class TaskThumbnail(val taskId: Int, val isRunning: Boolean)
+ data class Snapshot(
+ val bitmap: Bitmap,
+ @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 fcc2af3..41aee52 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -28,6 +28,7 @@
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.ViewPool
@@ -36,10 +37,12 @@
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.Uninitialized
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
import com.android.quickstep.util.TaskCornerRadius
import com.android.systemui.shared.system.QuickStepContract
+import kotlin.math.abs
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -50,6 +53,7 @@
class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
+ private val viewData: TaskThumbnailViewData by RecentsDependencies.inject(this)
private val viewModel: TaskThumbnailViewModel by RecentsDependencies.inject(this)
private lateinit var viewAttachedScope: CoroutineScope
@@ -57,6 +61,8 @@
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 var uiState: TaskThumbnailUiState = Uninitialized
private var inheritedScale: Float = 1f
@@ -92,16 +98,16 @@
when (viewModelUiState) {
is Uninitialized -> {}
is LiveTile -> drawLiveWindow()
- is Snapshot -> drawSnapshot(viewModelUiState)
+ is SnapshotSplash -> drawSnapshotSplash(viewModelUiState)
is BackgroundOnly -> drawBackground(viewModelUiState.backgroundColor)
}
}
.launchIn(viewAttachedScope)
viewModel.dimProgress
- .onEach { dimProgress ->
- // TODO(b/348195366) Add fade in/out for scrim
- scrimView.alpha = dimProgress * MAX_SCRIM_ALPHA
- }
+ .onEach { dimProgress -> scrimView.alpha = dimProgress }
+ .launchIn(viewAttachedScope)
+ viewModel.splashAlpha
+ .onEach { splashAlpha -> splashContainer.alpha = splashAlpha }
.launchIn(viewAttachedScope)
viewModel.cornerRadiusProgress.onEach { invalidateOutline() }.launchIn(viewAttachedScope)
viewModel.inheritedScale
@@ -129,13 +135,35 @@
uiState = Uninitialized
}
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ if (changed) {
+ viewData.width.value = abs(right - left)
+ viewData.height.value = abs(bottom - top)
+ }
+ }
+
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
- if (uiState is Snapshot) {
+ if (uiState is SnapshotSplash) {
setImageMatrix()
}
}
+ override fun setScaleX(scaleX: Float) {
+ super.setScaleX(scaleX)
+ viewData.scaleX.value = scaleX
+ // Splash icon should ignore scale
+ splashIcon.scaleX = 1 / scaleX
+ }
+
+ override fun setScaleY(scaleY: Float) {
+ super.setScaleY(scaleY)
+ viewData.scaleY.value = scaleY
+ // Splash icon should ignore scale
+ splashIcon.scaleY = 1 / scaleY
+ }
+
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
@@ -147,6 +175,7 @@
private fun resetViews() {
liveTileView.isVisible = false
thumbnailView.isVisible = false
+ splashContainer.alpha = 0f
scrimView.alpha = 0f
setBackgroundColor(Color.BLACK)
}
@@ -159,6 +188,18 @@
liveTileView.isVisible = true
}
+ 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
+ }
+ }
+
private fun drawSnapshot(snapshot: Snapshot) {
drawBackground(snapshot.backgroundColor)
thumbnailView.setImageBitmap(snapshot.bitmap)
@@ -176,8 +217,4 @@
overviewCornerRadius,
fullscreenCornerRadius
) / inheritedScale
-
- private companion object {
- const val MAX_SCRIM_ALPHA = 0.4f
- }
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt
new file mode 100644
index 0000000..1f8c0bc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewData.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.task.thumbnail
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+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/TaskContainerData.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskContainerData.kt
index 769424c..5f2de94 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskContainerData.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskContainerData.kt
@@ -20,4 +20,6 @@
class TaskContainerData {
val taskMenuOpenProgress = MutableStateFlow(0f)
+
+ val thumbnailSplashProgress = MutableStateFlow(0f)
}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
index 6465645..4e29840 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskThumbnailViewModel.kt
@@ -10,26 +10,32 @@
* 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
+ * See the License for the specific language goveryning permissions and
* limitations under the License.
*/
package com.android.quickstep.task.viewmodel
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.TaskThumbnail
+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
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,10 +53,13 @@
taskViewData: TaskViewData,
taskContainerData: TaskContainerData,
private val tasksRepository: RecentTasksRepository,
- private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase
+ private val getThumbnailPositionUseCase: GetThumbnailPositionUseCase,
+ private val splashAlphaUseCase: SplashAlphaUseCase,
+ private val getSplashSizeUseCase: GetSplashSizeUseCase,
) {
private val task = MutableStateFlow<Flow<Task?>>(flowOf(null))
- private lateinit var taskThumbnail: TaskThumbnail
+ private val splashProgress = MutableStateFlow(flowOf(0f))
+ private var taskId: Int = INVALID_TASK_ID
/**
* Progress for changes in corner radius. progress: 0 = overview corner radius; 1 = fullscreen
@@ -59,40 +68,55 @@
val cornerRadiusProgress =
if (taskViewData.isOutlineFormedByThumbnailView) recentsViewData.fullscreenProgress
else MutableStateFlow(1f).asStateFlow()
+
val inheritedScale =
combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale ->
recentsScale * taskScale
}
- val dimProgress: Flow<Float> = taskContainerData.taskMenuOpenProgress
+
+ val dimProgress: Flow<Float> =
+ combine(taskContainerData.taskMenuOpenProgress, recentsViewData.tintAmount) {
+ taskMenuOpenProgress,
+ tintAmount ->
+ max(taskMenuOpenProgress * MAX_SCRIM_ALPHA, tintAmount)
+ }
+ val splashAlpha = splashProgress.flatMapLatest { it }
+
+ private val isLiveTile =
+ combine(
+ task.flatMapLatest { it }.map { it?.key?.id }.distinctUntilChanged(),
+ recentsViewData.runningTaskIds,
+ recentsViewData.runningTaskShowScreenshot
+ ) { taskId, runningTaskIds, runningTaskShowScreenshot ->
+ runningTaskIds.contains(taskId) && !runningTaskShowScreenshot
+ }
+ .distinctUntilChanged()
+
val uiState: Flow<TaskThumbnailUiState> =
- task
- .flatMapLatest { taskFlow ->
- taskFlow.map { taskVal ->
- when {
- taskVal == null -> Uninitialized
- taskThumbnail.isRunning -> LiveTile
- isBackgroundOnly(taskVal) ->
- BackgroundOnly(taskVal.colorBackground.removeAlpha())
- isSnapshotState(taskVal) -> {
- val bitmap = taskVal.thumbnail?.thumbnail!!
- Snapshot(bitmap, taskVal.colorBackground.removeAlpha())
- }
- else -> Uninitialized
- }
+ combine(task.flatMapLatest { it }, isLiveTile) { taskVal, isRunning ->
+ when {
+ taskVal == null -> Uninitialized
+ isRunning -> LiveTile
+ isBackgroundOnly(taskVal) ->
+ BackgroundOnly(taskVal.colorBackground.removeAlpha())
+ isSnapshotSplashState(taskVal) ->
+ SnapshotSplash(createSnapshotState(taskVal), createSplashState(taskVal))
+ else -> Uninitialized
}
}
.distinctUntilChanged()
- fun bind(taskThumbnail: TaskThumbnail) {
- this.taskThumbnail = taskThumbnail
- task.value = tasksRepository.getTaskDataById(taskThumbnail.taskId)
+ fun bind(taskId: Int) {
+ this.taskId = taskId
+ task.value = tasksRepository.getTaskDataById(taskId)
+ splashProgress.value = splashAlphaUseCase.execute(taskId)
}
fun getThumbnailPositionState(width: Int, height: Int, isRtl: Boolean): Matrix {
return runBlocking {
when (
val thumbnailPositionState =
- getThumbnailPositionUseCase.run(taskThumbnail.taskId, width, height, isRtl)
+ getThumbnailPositionUseCase.run(taskId, width, height, isRtl)
) {
is ThumbnailPositionState.MatrixScaling -> thumbnailPositionState.matrix
is ThumbnailPositionState.MissingThumbnail -> Matrix.IDENTITY_MATRIX
@@ -102,12 +126,28 @@
private fun isBackgroundOnly(task: Task): Boolean = task.isLocked || task.thumbnail == null
- private fun isSnapshotState(task: Task): Boolean {
+ private fun isSnapshotSplashState(task: Task): Boolean {
val thumbnailPresent = task.thumbnail?.thumbnail != null
val taskLocked = task.isLocked
return thumbnailPresent && !taskLocked
}
+ private fun createSnapshotState(task: Task): Snapshot {
+ val thumbnailData = task.thumbnail
+ val bitmap = thumbnailData?.thumbnail!!
+ 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 {
+ const val MAX_SCRIM_ALPHA = 0.4f
+ }
}
diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
index 7a9ecf2..07dfc29 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt
@@ -23,6 +23,8 @@
// 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 ec75d59..30ee360 100644
--- a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewModel.kt
@@ -22,4 +22,8 @@
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/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index 8e3d44f..31aca03 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -17,18 +17,28 @@
package com.android.quickstep.util;
import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.animation.AnimatorSet;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.animation.Interpolator;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.views.RecentsViewContainer;
/**
* Utility class containing methods to help manage animations, interpolators, and timings.
*/
public class AnimUtils {
+ private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
+
/**
* Fetches device-specific timings for the Overview > Split animation
* (splitscreen initiated from Overview).
@@ -59,6 +69,33 @@
}
/**
+ * Synchronizes the timing for the split dismiss animation to the current transition to
+ * NORMAL (launcher home/workspace)
+ */
+ public static void goToNormalStateWithSplitDismissal(@NonNull StateManager stateManager,
+ @NonNull RecentsViewContainer container,
+ @NonNull StatsLogManager.LauncherEvent exitReason,
+ @NonNull SplitAnimationController animationController) {
+ StateAnimationConfig config = new StateAnimationConfig();
+ BaseState startState = stateManager.getState();
+ long duration = startState.getTransitionDuration(container.asContext(),
+ false /*isToState*/);
+ if (duration == 0) {
+ // Case where we're in contextual on workspace (NORMAL), which by default has 0
+ // transition duration
+ duration = DURATION_DEFAULT_SPLIT_DISMISS;
+ }
+ config.duration = duration;
+ AnimatorSet stateAnim = stateManager.createAtomicAnimation(
+ startState, NORMAL, config);
+ AnimatorSet dismissAnim = animationController
+ .createPlaceholderDismissAnim(container, exitReason, duration);
+ stateAnim.play(dismissAnim);
+ stateManager.setCurrentAnimation(stateAnim, NORMAL);
+ stateAnim.start();
+ }
+
+ /**
* Returns a IRemoteCallback which completes the provided list as a result
*/
public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
diff --git a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
index d36dc7e..2dd727e 100644
--- a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
+++ b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
@@ -26,19 +26,21 @@
import androidx.annotation.WorkerThread
import com.android.launcher3.BuildConfig
import com.android.launcher3.util.Executors
+import java.util.concurrent.CopyOnWriteArrayList
/** Utility class to manage a set of device configurations */
class DeviceConfigHelper<ConfigType>(private val factory: (PropReader) -> ConfigType) {
var config: ConfigType
private set
+
private val allKeys: Set<String>
private val propertiesListener = OnPropertiesChangedListener { onDevicePropsChanges(it) }
private val sharedPrefChangeListener = OnSharedPreferenceChangeListener { _, _ ->
recreateConfig()
}
- private val changeListeners = mutableListOf<Runnable>()
+ private val changeListeners = CopyOnWriteArrayList<Runnable>()
init {
// Initialize the default config once.
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index b8bc828..15081da 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -66,6 +66,7 @@
private Float mPreviousVelocity = null;
private OnMotionPauseListener mOnMotionPauseListener;
+ private boolean mIsTrackpadGesture;
private boolean mIsPaused;
// Bias more for the first pause to make it feel extra responsive.
private boolean mHasEverBeenPaused;
@@ -115,6 +116,10 @@
mOnMotionPauseListener = listener;
}
+ public void setIsTrackpadGesture(boolean isTrackpadGesture) {
+ mIsTrackpadGesture = isTrackpadGesture;
+ }
+
/**
* @param disallowPause If true, we will not detect any pauses until this is set to false again.
*/
@@ -179,7 +184,8 @@
// We want to be more aggressive about detecting the first pause to ensure it
// feels as responsive as possible; getting two very slow speeds back to back
// takes too long, so also check for a rapid deceleration.
- boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
+ boolean isRapidDeceleration =
+ speed < previousSpeed * getRapidDecelerationFactor();
isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
isPausedReason = new ActiveGestureLog.CompoundString(
"Didn't have back to back slow speeds, checking for rapid ")
@@ -253,6 +259,7 @@
mVelocityProvider.clear();
mPreviousVelocity = null;
setOnMotionPauseListener(null);
+ mIsTrackpadGesture = false;
mIsPaused = mHasEverBeenPaused = false;
mSlowStartTime = 0;
mForcePauseTimeout.cancelAlarm();
@@ -262,6 +269,12 @@
return mIsPaused;
}
+ private float getRapidDecelerationFactor() {
+ return mIsTrackpadGesture ? Float.parseFloat(
+ Utilities.getSystemProperty("trackpad_in_app_swipe_up_deceleration_factor",
+ String.valueOf(RAPID_DECELERATION_FACTOR))) : RAPID_DECELERATION_FACTOR;
+ }
+
public interface OnMotionPauseListener {
/** Called only the first time motion pause is detected. */
void onMotionPauseDetected();
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index cfe5b2c..70ef47c 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -106,7 +106,8 @@
return;
}
mShouldIncreaseCount = toState == HINT_STATE
- && launcher.getWorkspace().getNextPage() == Workspace.DEFAULT_PAGE;
+ && launcher.getWorkspace().getNextPage() == Workspace.DEFAULT_PAGE
+ && launcher.isCanShowAllAppsEducationView();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index e31a828..0335fa1 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -187,7 +187,6 @@
) {
val snapshot = taskContainer.snapshotView
val iconView: View = taskContainer.iconView.asView()
- // TODO(334826842): Switch to splash state in TaskThumbnailView
if (!enableRefactorTaskThumbnail()) {
val thumbnailViewDeprecated = taskContainer.thumbnailViewDeprecated
builder.add(
@@ -198,6 +197,15 @@
)
)
thumbnailViewDeprecated.setShowSplashForSplitSelection(true)
+ } else {
+ builder.add(
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ addUpdateListener {
+ taskContainer.taskContainerData.thumbnailSplashProgress.value =
+ it.animatedFraction
+ }
+ }
+ )
}
// With the new `IconAppChipView`, we always want to keep the chip pinned to the
// top left of the task / thumbnail.
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d906bb3..54f2dd3 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -16,7 +16,6 @@
package com.android.quickstep.util;
-import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTED_SECOND_APP;
@@ -60,8 +59,6 @@
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.RemoteTransition;
@@ -94,13 +91,11 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SplitSelectionListener;
import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.SplitInstructionsView;
-import com.android.systemui.animation.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@@ -276,17 +271,15 @@
// Loop through tasks in reverse, since they are ordered with recent tasks last
for (int j = taskGroups.size() - 1; j >= 0; j--) {
GroupTask groupTask = taskGroups.get(j);
- Task task1 = groupTask.task1;
- // Don't add duplicate Tasks
- if (isInstanceOfComponent(task1, key)
- && !Arrays.asList(lastActiveTasks).contains(task1)) {
- lastActiveTask = task1;
- break;
+ // Account for desktop cases where there can be N tasks in the group
+ for (Task task : groupTask.getTasks()) {
+ if (isInstanceOfComponent(task, key)
+ && !Arrays.asList(lastActiveTasks).contains(task)) {
+ lastActiveTask = task;
+ break;
+ }
}
- Task task2 = groupTask.task2;
- if (isInstanceOfComponent(task2, key)
- && !Arrays.asList(lastActiveTasks).contains(task2)) {
- lastActiveTask = task2;
+ if (lastActiveTask != null) {
break;
}
}
@@ -460,77 +453,41 @@
Bundle optionsBundle = options1.toBundle();
Bundle extrasBundle = new Bundle(1);
extrasBundle.putParcelable(KEY_EXTRA_WIDGET_INTENT, widgetIntent);
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId,
- secondTaskId, callback, "LaunchSplitPair");
- switch (launchData.getSplitLaunchType()) {
- case SPLIT_TASK_TASK ->
- mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId,
- null /* options2 */, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
+ final RemoteTransition remoteTransition = getRemoteTransition(firstTaskId,
+ secondTaskId, callback, "LaunchSplitPair");
+ switch (launchData.getSplitLaunchType()) {
+ case SPLIT_TASK_TASK ->
+ mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId,
+ null /* options2 */, initialStagePosition, snapPosition,
+ remoteTransition, shellInstanceId);
- case SPLIT_TASK_PENDINGINTENT ->
- mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle,
- firstTaskId, extrasBundle, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
+ case SPLIT_TASK_PENDINGINTENT ->
+ mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle,
+ firstTaskId, extrasBundle, initialStagePosition, snapPosition,
+ remoteTransition, shellInstanceId);
- case SPLIT_TASK_SHORTCUT ->
- mSystemUiProxy.startShortcutAndTask(secondShortcut, optionsBundle,
- firstTaskId, null /*options2*/, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
+ case SPLIT_TASK_SHORTCUT ->
+ mSystemUiProxy.startShortcutAndTask(secondShortcut, optionsBundle,
+ firstTaskId, null /*options2*/, initialStagePosition, snapPosition,
+ remoteTransition, shellInstanceId);
- case SPLIT_PENDINGINTENT_TASK ->
- mSystemUiProxy.startIntentAndTask(firstPI, firstUserId, optionsBundle,
- secondTaskId, null /*options2*/, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
+ case SPLIT_PENDINGINTENT_TASK ->
+ mSystemUiProxy.startIntentAndTask(firstPI, firstUserId, optionsBundle,
+ secondTaskId, null /*options2*/, initialStagePosition, snapPosition,
+ remoteTransition, shellInstanceId);
- case SPLIT_PENDINGINTENT_PENDINGINTENT ->
- mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut,
- optionsBundle, secondPI, secondUserId, secondShortcut, extrasBundle,
- initialStagePosition, snapPosition, remoteTransition,
- shellInstanceId);
+ case SPLIT_PENDINGINTENT_PENDINGINTENT ->
+ mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut,
+ optionsBundle, secondPI, secondUserId, secondShortcut, extrasBundle,
+ initialStagePosition, snapPosition, remoteTransition,
+ shellInstanceId);
- case SPLIT_SHORTCUT_TASK ->
- mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle,
- secondTaskId, null /*options2*/, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
- }
- } else {
- final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId, secondTaskId,
- callback);
- switch (launchData.getSplitLaunchType()) {
- case SPLIT_TASK_TASK ->
- mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle,
- secondTaskId, null /* options2 */, initialStagePosition,
- snapPosition, adapter, shellInstanceId);
-
- case SPLIT_TASK_PENDINGINTENT ->
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(secondPI,
- secondUserId, optionsBundle, firstTaskId, null /*options2*/,
- initialStagePosition, snapPosition, adapter, shellInstanceId);
-
- case SPLIT_TASK_SHORTCUT ->
- mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(secondShortcut,
- optionsBundle, firstTaskId, null /*options2*/, initialStagePosition,
- snapPosition, adapter, shellInstanceId);
-
- case SPLIT_PENDINGINTENT_TASK ->
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId,
- optionsBundle, secondTaskId, null /*options2*/,
- initialStagePosition, snapPosition, adapter, shellInstanceId);
-
- case SPLIT_PENDINGINTENT_PENDINGINTENT ->
- mSystemUiProxy.startIntentsWithLegacyTransition(firstPI, firstUserId,
- firstShortcut, optionsBundle, secondPI, secondUserId,
- secondShortcut, null /*options2*/, initialStagePosition,
- snapPosition, adapter, shellInstanceId);
-
- case SPLIT_SHORTCUT_TASK ->
- mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(firstShortcut,
- optionsBundle, secondTaskId, null /*options2*/,
- initialStagePosition, snapPosition, adapter, shellInstanceId);
- }
+ case SPLIT_SHORTCUT_TASK ->
+ mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle,
+ secondTaskId, null /*options2*/, initialStagePosition, snapPosition,
+ remoteTransition, shellInstanceId);
}
+
}
/**
@@ -576,20 +533,13 @@
}
Bundle optionsBundle = options1.toBundle();
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- final RemoteTransition transition = remoteTransition == null
- ? getShellRemoteTransition(
- firstTaskId, secondTaskId, callback, "LaunchExistingPair")
- : remoteTransition;
- mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */,
- stagePosition, snapPosition, transition, null /*shellInstanceId*/);
- } else {
- final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId,
- secondTaskId, callback);
- mSystemUiProxy.startTasksWithLegacyTransition(firstTaskId, optionsBundle, secondTaskId,
- null /* options2 */, stagePosition, snapPosition, adapter,
- null /*shellInstanceId*/);
- }
+ final RemoteTransition transition = remoteTransition == null
+ ? getRemoteTransition(
+ firstTaskId, secondTaskId, callback, "LaunchExistingPair")
+ : remoteTransition;
+ mSystemUiProxy.startTasks(firstTaskId, optionsBundle, secondTaskId, null /* options2 */,
+ stagePosition, snapPosition, transition, null /*shellInstanceId*/);
+
}
/**
@@ -615,34 +565,16 @@
ActivityThread.currentActivityThread().getApplicationThread(),
"LaunchAppFullscreen");
InstanceId instanceId = mSessionInstanceIds.first;
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- switch (launchData.getSplitLaunchType()) {
- case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasks(firstTaskId,
- optionsBundle, secondTaskId, null /* options2 */, initialStagePosition,
- SNAP_TO_50_50, remoteTransition, instanceId);
- case SPLIT_SINGLE_INTENT_FULLSCREEN -> mSystemUiProxy.startIntentAndTask(firstPI,
- firstUserId, optionsBundle, secondTaskId, null /*options2*/,
- initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId);
- case SPLIT_SINGLE_SHORTCUT_FULLSCREEN -> mSystemUiProxy.startShortcutAndTask(
- initialShortcut, optionsBundle, firstTaskId, null /* options2 */,
- initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId);
- }
- } else {
- final RemoteAnimationAdapter adapter = getLegacyRemoteAdapter(firstTaskId,
- secondTaskId, callback);
- switch (launchData.getSplitLaunchType()) {
- case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasksWithLegacyTransition(
- firstTaskId, optionsBundle, secondTaskId, null /* options2 */,
- initialStagePosition, SNAP_TO_50_50, adapter, instanceId);
- case SPLIT_SINGLE_INTENT_FULLSCREEN ->
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId,
- optionsBundle, secondTaskId, null /*options2*/,
- initialStagePosition, SNAP_TO_50_50, adapter, instanceId);
- case SPLIT_SINGLE_SHORTCUT_FULLSCREEN ->
- mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(
- initialShortcut, optionsBundle, firstTaskId, null /* options2 */,
- initialStagePosition, SNAP_TO_50_50, adapter, instanceId);
- }
+ switch (launchData.getSplitLaunchType()) {
+ case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasks(firstTaskId,
+ optionsBundle, secondTaskId, null /* options2 */, initialStagePosition,
+ SNAP_TO_50_50, remoteTransition, instanceId);
+ case SPLIT_SINGLE_INTENT_FULLSCREEN -> mSystemUiProxy.startIntentAndTask(firstPI,
+ firstUserId, optionsBundle, secondTaskId, null /*options2*/,
+ initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId);
+ case SPLIT_SINGLE_SHORTCUT_FULLSCREEN -> mSystemUiProxy.startShortcutAndTask(
+ initialShortcut, optionsBundle, firstTaskId, null /* options2 */,
+ initialStagePosition, SNAP_TO_50_50, remoteTransition, instanceId);
}
}
@@ -660,7 +592,7 @@
mSplitFromDesktopController = controller;
}
- private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId,
+ private RemoteTransition getRemoteTransition(int firstTaskId, int secondTaskId,
@Nullable Consumer<Boolean> callback, String transitionName) {
final RemoteSplitLaunchTransitionRunner animationRunner =
new RemoteSplitLaunchTransitionRunner(firstTaskId, secondTaskId, callback);
@@ -668,14 +600,6 @@
ActivityThread.currentActivityThread().getApplicationThread(), transitionName);
}
- private RemoteAnimationAdapter getLegacyRemoteAdapter(int firstTaskId, int secondTaskId,
- @Nullable Consumer<Boolean> callback) {
- final RemoteSplitLaunchAnimationRunner animationRunner =
- new RemoteSplitLaunchAnimationRunner(firstTaskId, secondTaskId, callback);
- return new RemoteAnimationAdapter(animationRunner, 300, 150,
- ActivityThread.currentActivityThread().getApplicationThread());
- }
-
/**
* Will initialize {@link #mSessionInstanceIds} if null and log the first split event from
* {@link #mSplitSelectDataHolder}
@@ -807,55 +731,6 @@
}
/**
- * LEGACY
- * Remote animation runner for animation to launch an app.
- */
- private class RemoteSplitLaunchAnimationRunner extends RemoteAnimationRunnerCompat {
-
- private final int mInitialTaskId;
- private final int mSecondTaskId;
- private final Consumer<Boolean> mSuccessCallback;
-
- RemoteSplitLaunchAnimationRunner(int initialTaskId, int secondTaskId,
- @Nullable Consumer<Boolean> successCallback) {
- mInitialTaskId = initialTaskId;
- mSecondTaskId = secondTaskId;
- mSuccessCallback = successCallback;
- }
-
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- Runnable finishedCallback) {
- postAsyncCallback(mHandler,
- () -> mSplitAnimationController
- .playSplitLaunchAnimation(mLaunchingTaskView,
- mLaunchingIconView, mInitialTaskId, mSecondTaskId, apps, wallpapers,
- nonApps, mStateManager, mDepthController, null /* info */, null /* t */,
- () -> {
- finishedCallback.run();
- if (mSuccessCallback != null) {
- mSuccessCallback.accept(true);
- }
- resetState();
- },
- QuickStepContract.getWindowCornerRadius(mContainer.asContext())));
- }
-
- @Override
- public void onAnimationCancelled() {
- postAsyncCallback(mHandler, () -> {
- if (mSuccessCallback != null) {
- // Launching legacy tasks while recents animation is running will always cause
- // onAnimationCancelled to be called (should be fixed w/ shell transitions?)
- mSuccessCallback.accept(mRecentsAnimationRunning);
- }
- resetState();
- });
- }
- }
-
- /**
* To be called whenever we exit split selection state. If
* {@link FeatureFlags#enableSplitContextually()} is set, this should be the
* central way split is getting reset, which should then go through the callbacks to reset
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index e48a7c6..d20d0a5 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -26,7 +26,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
-import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY;
import android.annotation.TargetApi;
import android.content.Context;
@@ -54,6 +54,7 @@
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.systemui.shared.recents.model.Task;
@@ -91,10 +92,12 @@
protected void handleStartHome(boolean animated) {
StateManager stateManager = getStateManager();
animated &= stateManager.shouldAnimateStateChange();
- stateManager.goToState(NORMAL, animated);
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.getSplitAnimationController()
- .playPlaceholderDismissAnim(mContainer, LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
+ if (mSplitSelectStateController.isSplitSelectActive()) {
+ AnimUtils.goToNormalStateWithSplitDismissal(stateManager, mContainer,
+ LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
+ mSplitSelectStateController.getSplitAnimationController());
+ } else {
+ stateManager.goToState(NORMAL, animated);
}
AbstractFloatingView.closeAllOpenViews(mContainer, animated);
}
@@ -265,7 +268,7 @@
super.onGestureAnimationStart(runningTasks, rotationTouchHelper);
DesktopVisibilityController desktopVisibilityController =
mContainer.getDesktopVisibilityController();
- if (!enableDesktopWindowingWallpaperActivity() && desktopVisibilityController != null) {
+ if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && desktopVisibilityController != null) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureStart();
}
@@ -288,7 +291,7 @@
}
}
super.onGestureAnimationEnd();
- if (!enableDesktopWindowingWallpaperActivity() && desktopVisibilityController != null) {
+ if (!WALLPAPER_ACTIVITY.isEnabled(mContext) && desktopVisibilityController != null) {
// TODO: b/333533253 - Remove after flag rollout
desktopVisibilityController.setRecentsGestureEnd(endTarget);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a3d6359..3702f93 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -193,7 +193,9 @@
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.recents.data.RecentTasksRepository;
import com.android.quickstep.recents.data.RecentsDeviceProfileRepository;
+import com.android.quickstep.recents.data.RecentsDeviceProfileRepositoryImpl;
import com.android.quickstep.recents.data.RecentsRotationStateRepository;
+import com.android.quickstep.recents.data.RecentsRotationStateRepositoryImpl;
import com.android.quickstep.recents.di.RecentsDependencies;
import com.android.quickstep.recents.viewmodel.RecentsViewData;
import com.android.quickstep.recents.viewmodel.RecentsViewModel;
@@ -231,6 +233,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -805,6 +808,7 @@
private boolean mAnyTaskHasBeenDismissed;
private final RecentsViewModel mRecentsViewModel;
+ private final RecentsViewHelper mHelper;
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
BaseContainerInterface sizeStrategy) {
@@ -826,14 +830,16 @@
recentsDependencies.inject(RecentTasksRepository.class),
recentsDependencies.inject(RecentsViewData.class)
);
+ mHelper = new RecentsViewHelper(mRecentsViewModel);
recentsDependencies.provide(RecentsRotationStateRepository.class,
- () -> new RecentsRotationStateRepository(mOrientationState));
+ () -> new RecentsRotationStateRepositoryImpl(mOrientationState));
recentsDependencies.provide(RecentsDeviceProfileRepository.class,
- () -> new RecentsDeviceProfileRepository(mContainer));
+ () -> new RecentsDeviceProfileRepositoryImpl(mContainer));
} else {
mRecentsViewModel = null;
+ mHelper = null;
}
mScrollHapticMinGapMillis = getResources()
@@ -1041,8 +1047,11 @@
@Override
@Nullable
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+ if (enableRefactorTaskThumbnail()) {
+ // TODO(b/342560598): Listen in TaskRepository and reload
+ return null;
+ }
if (mHandleTaskStackChanges) {
- // TODO(b/342560598): Handle onTaskThumbnailChanged for new TTV.
if (!enableRefactorTaskThumbnail()) {
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
@@ -1061,6 +1070,7 @@
@Override
public void onTaskIconChanged(String pkg, UserHandle user) {
+ // TODO(b/342560598): Listen in TaskRepository and reload.
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = requireTaskViewAt(i);
Task task = tv.getFirstTask();
@@ -1076,47 +1086,38 @@
@Override
public void onTaskIconChanged(int taskId) {
+ if (enableRefactorTaskThumbnail()) {
+ return;
+ }
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
taskView.refreshTaskThumbnailSplash();
}
}
- /**
- * Update the thumbnail(s) of the relevant TaskView.
- *
- * @param refreshNow Refresh immediately if it's true.
- */
- @Nullable
- public TaskView updateThumbnail(
- HashMap<Integer, ThumbnailData> thumbnailData, boolean refreshNow) {
+ /** Updates the thumbnail(s) of the relevant TaskView. */
+ public void updateThumbnail(Map<Integer, ThumbnailData> thumbnailData) {
if (enableRefactorTaskThumbnail()) {
- // TODO(b/342560598): Handle updateThumbnail for new TTV.
- return null;
- }
- TaskView updatedTaskView = null;
- for (Map.Entry<Integer, ThumbnailData> entry : thumbnailData.entrySet()) {
- Integer id = entry.getKey();
- ThumbnailData thumbnail = entry.getValue();
- TaskView taskView = getTaskViewByTaskId(id);
- if (taskView == null) {
- continue;
+ mRecentsViewModel.addOrUpdateThumbnailOverride(thumbnailData);
+ } else {
+ for (Map.Entry<Integer, ThumbnailData> entry : thumbnailData.entrySet()) {
+ Integer id = entry.getKey();
+ ThumbnailData thumbnail = entry.getValue();
+ TaskView taskView = getTaskViewByTaskId(id);
+ if (taskView == null) {
+ continue;
+ }
+ // taskView could be a GroupedTaskView, so select the relevant task by ID
+ TaskContainer taskContainer = taskView.getTaskContainerById(id);
+ if (taskContainer == null) {
+ continue;
+ }
+ Task task = taskContainer.getTask();
+ TaskThumbnailViewDeprecated taskThumbnailViewDeprecated =
+ taskContainer.getThumbnailViewDeprecated();
+ taskThumbnailViewDeprecated.setThumbnail(task, thumbnail, /*refreshNow=*/false);
}
- // taskView could be a GroupedTaskView, so select the relevant task by ID
- TaskContainer taskAttributes = taskView.getTaskContainerById(id);
- if (taskAttributes == null) {
- continue;
- }
- Task task = taskAttributes.getTask();
- TaskThumbnailViewDeprecated taskThumbnailViewDeprecated =
- taskAttributes.getThumbnailViewDeprecated();
- taskThumbnailViewDeprecated.setThumbnail(task, thumbnail, refreshNow);
- // thumbnailData can contain 1-2 ids, but they should correspond to the same
- // TaskView, so overwriting is ok
- updatedTaskView = taskView;
}
-
- return updatedTaskView;
}
@Override
@@ -1171,6 +1172,9 @@
if (FeatureFlags.enableSplitContextually()) {
mSplitSelectStateController.registerSplitListener(mSplitSelectionListener);
}
+ if (enableRefactorTaskThumbnail()) {
+ mHelper.onAttachedToWindow();
+ }
}
@Override
@@ -1193,6 +1197,9 @@
mSplitSelectStateController.unregisterSplitListener(mSplitSelectionListener);
}
reset();
+ if (enableRefactorTaskThumbnail()) {
+ mHelper.onDetachedFromWindow();
+ }
}
@Override
@@ -2428,10 +2435,6 @@
List<Task> tasksToUpdate = containers.stream()
.map(TaskContainer::getTask)
.collect(Collectors.toCollection(ArrayList::new));
- if (enableRefactorTaskThumbnail()) {
- visibleTaskIds.addAll(
- tasksToUpdate.stream().map((task) -> task.key.id).toList());
- }
if (mTmpRunningTasks != null) {
for (Task t : mTmpRunningTasks) {
// Skip loading if this is the task that we are animating into
@@ -2439,6 +2442,10 @@
tasksToUpdate.removeIf(task -> task == t);
}
}
+ if (enableRefactorTaskThumbnail()) {
+ visibleTaskIds.addAll(
+ tasksToUpdate.stream().map((task) -> task.key.id).toList());
+ }
if (tasksToUpdate.isEmpty()) {
continue;
}
@@ -2495,6 +2502,11 @@
mModel.preloadCacheIfNeeded();
}
+ if (enableRefactorTaskThumbnail()) {
+ // TODO(b/342560598): Listen in TaskRepository and reload.
+ return;
+ }
+
// Whenever the high res loading state changes, poke each of the visible tasks to see if
// they want to updated their thumbnail state
for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
@@ -2563,14 +2575,19 @@
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
- post(() -> {
- unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
- setCurrentPage(0);
- LayoutUtils.setViewEnabled(mActionsView, true);
- if (mOrientationState.setGestureActive(false)) {
- updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
- }
- });
+ post(this::onReset);
+ }
+
+ private void onReset() {
+ if (enableRefactorTaskThumbnail()) {
+ mRecentsViewModel.onReset();
+ }
+ unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+ setCurrentPage(0);
+ LayoutUtils.setViewEnabled(mActionsView, true);
+ if (mOrientationState.setGestureActive(false)) {
+ updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
+ }
}
public int getRunningTaskViewId() {
@@ -2946,18 +2963,13 @@
}
private void setRunningTaskViewId(int runningTaskViewId) {
- int prevRunningTaskViewId = mRunningTaskViewId;
mRunningTaskViewId = runningTaskViewId;
if (enableRefactorTaskThumbnail()) {
- TaskView previousRunningTaskView = getTaskViewFromTaskViewId(prevRunningTaskViewId);
- if (previousRunningTaskView != null) {
- previousRunningTaskView.notifyIsRunningTaskUpdated();
- }
- TaskView newRunningTaskView = getTaskViewFromTaskViewId(runningTaskViewId);
- if (newRunningTaskView != null) {
- newRunningTaskView.notifyIsRunningTaskUpdated();
- }
+ TaskView runningTaskView = getTaskViewFromTaskViewId(runningTaskViewId);
+ mRecentsViewModel.updateRunningTask(
+ runningTaskView != null ? runningTaskView.getTaskIdSet()
+ : Collections.emptySet());
}
}
@@ -2987,6 +2999,9 @@
if (runningTaskView != null) {
runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot);
}
+ if (enableRefactorTaskThumbnail()) {
+ mRecentsViewModel.setRunningTaskShowScreenshot(showScreenshot);
+ }
}
public void setTaskIconScaledDown(boolean isScaledDown) {
@@ -3297,6 +3312,10 @@
}
private void setTaskThumbnailSplashAlpha(float taskThumbnailSplashAlpha) {
+ if (enableRefactorTaskThumbnail()) {
+ mRecentsViewModel.updateThumbnailSplashProgress(taskThumbnailSplashAlpha);
+ return;
+ }
int taskCount = getTaskViewCount();
if (taskCount == 0) {
return;
@@ -4894,7 +4913,6 @@
mSplitHiddenTaskView.getWidth(), mSplitHiddenTaskView.getHeight(),
primaryTaskSelected);
builder.addOnFrameCallback(() -> {
- // TODO(b/334826842): Handle splash icon for new TTV.
if (!enableRefactorTaskThumbnail()) {
taskContainer.getThumbnailViewDeprecated().refreshSplashView();
}
@@ -5498,7 +5516,7 @@
}
RemoteTargetGluer gluer;
- if (recentsAnimationTargets.hasDesktopTasks()) {
+ if (recentsAnimationTargets.hasDesktopTasks(mContext)) {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
true /* forDesktop */);
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
@@ -6019,38 +6037,28 @@
return;
}
- switchToScreenshotInternal(onFinishRunnable);
- }
-
- private void switchToScreenshotInternal(Runnable onFinishRunnable) {
- // TODO(b/342560598): Handle switchToScreenshot for new TTV.
- if (enableRefactorTaskThumbnail()) {
- onFinishRunnable.run();
- return;
- }
-
TaskView taskView = getRunningTaskView();
if (taskView == null) {
onFinishRunnable.run();
return;
}
- setRunningTaskViewShowScreenshot(true);
- for (TaskContainer container : taskView.getTaskContainers()) {
- if (container == null) {
- continue;
+ if (enableRefactorTaskThumbnail()) {
+ mHelper.switchToScreenshot(taskView, mRecentsAnimationController, onFinishRunnable);
+ } else {
+ setRunningTaskViewShowScreenshot(true);
+ for (TaskContainer container : taskView.getTaskContainers()) {
+ ThumbnailData thumbnailData =
+ mRecentsAnimationController.screenshotTask(container.getTask().key.id);
+ TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailViewDeprecated();
+ if (thumbnailData != null) {
+ thumbnailView.setThumbnail(container.getTask(), thumbnailData);
+ } else {
+ thumbnailView.refresh();
+ }
}
-
- ThumbnailData td =
- mRecentsAnimationController.screenshotTask(container.getTask().key.id);
- TaskThumbnailViewDeprecated thumbnailView = container.getThumbnailViewDeprecated();
- if (td != null) {
- thumbnailView.setThumbnail(container.getTask(), td);
- } else {
- thumbnailView.refresh();
- }
+ ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
}
- ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
}
/**
@@ -6064,9 +6072,13 @@
Runnable onFinishRunnable) {
final TaskView taskView = getRunningTaskView();
if (taskView != null) {
- taskView.setShouldShowScreenshot(true);
- taskView.refreshThumbnails(thumbnailDatas);
- ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
+ if (enableRefactorTaskThumbnail()) {
+ mHelper.switchToScreenshot(taskView, thumbnailDatas, onFinishRunnable);
+ } else {
+ taskView.setShouldShowScreenshot(true);
+ taskView.refreshThumbnails(thumbnailDatas);
+ ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
+ }
} else {
onFinishRunnable.run();
}
@@ -6116,7 +6128,6 @@
* tasks to be dimmed while other elements in the recents view are left alone.
*/
public void showForegroundScrim(boolean show) {
- // TODO(b/349601769) Add scrim response into new TTV - this is called from overlay
if (!show && mColorTint == 0) {
if (mTintingAnimator != null) {
mTintingAnimator.cancel();
@@ -6135,6 +6146,10 @@
private void setColorTint(float tintAmount) {
mColorTint = tintAmount;
+ if (enableRefactorTaskThumbnail()) {
+ mRecentsViewModel.setTintAmount(tintAmount);
+ }
+
for (int i = 0; i < getTaskViewCount(); i++) {
requireTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewHelper.kt b/quickstep/src/com/android/quickstep/views/RecentsViewHelper.kt
new file mode 100644
index 0000000..a63ccec
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewHelper.kt
@@ -0,0 +1,74 @@
+/*
+ * 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 com.android.quickstep.RecentsAnimationController
+import com.android.quickstep.ViewUtils
+import com.android.quickstep.recents.viewmodel.RecentsViewModel
+import com.android.systemui.shared.recents.model.ThumbnailData
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+
+/** Helper for [RecentsView] to interact with coroutine. */
+class RecentsViewHelper(private val recentsViewModel: RecentsViewModel) {
+ private lateinit var viewAttachedScope: CoroutineScope
+
+ fun onAttachedToWindow() {
+ viewAttachedScope =
+ CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("RecentsView"))
+ }
+
+ fun onDetachedFromWindow() {
+ viewAttachedScope.cancel("RecentsView detaching from window")
+ }
+
+ fun switchToScreenshot(
+ taskView: TaskView,
+ recentsAnimationController: RecentsAnimationController,
+ onFinishRunnable: Runnable
+ ) {
+ val updatedThumbnails =
+ taskView.taskContainers.associate {
+ it.task.key.id to recentsAnimationController.screenshotTask(it.task.key.id)
+ }
+ switchToScreenshot(taskView, updatedThumbnails, onFinishRunnable)
+ }
+
+ fun switchToScreenshot(
+ taskView: TaskView,
+ updatedThumbnails: Map<Int, ThumbnailData>?,
+ onFinishRunnable: Runnable
+ ) {
+ // Update recentsViewModel and apply the thumbnailOverride ASAP, before waiting inside
+ // viewAttachedScope.
+ recentsViewModel.setRunningTaskShowScreenshot(true)
+ if (updatedThumbnails != null) {
+ recentsViewModel.addOrUpdateThumbnailOverride(updatedThumbnails)
+ }
+ viewAttachedScope.launch {
+ recentsViewModel.waitForRunningTaskShowScreenshotToUpdate()
+ if (updatedThumbnails != null) {
+ recentsViewModel.waitForThumbnailsToUpdate(updatedThumbnails)
+ }
+ ViewUtils.postFrameDrawn(taskView, onFinishRunnable)
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 3d994e8..f6393e4 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,13 +16,11 @@
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON;
import static com.android.settingslib.widget.theme.R.dimen.settingslib_preferred_minimum_touch_target;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -42,9 +40,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
/**
@@ -57,7 +54,6 @@
public class SplitInstructionsView extends LinearLayout {
private static final int BOUNCE_DURATION = 250;
private static final float BOUNCE_HEIGHT = 20;
- private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
private final RecentsViewContainer mContainer;
public boolean mIsCurrentlyAnimating = false;
@@ -165,25 +161,11 @@
private void exitSplitSelection() {
RecentsView recentsView = mContainer.getOverviewPanel();
SplitSelectStateController splitSelectController = recentsView.getSplitSelectController();
-
StateManager stateManager = recentsView.getStateManager();
- BaseState startState = stateManager.getState();
- long duration = startState.getTransitionDuration(mContainer.asContext(), false);
- if (duration == 0) {
- // Case where we're in contextual on workspace (NORMAL), which by default has 0
- // transition duration
- duration = DURATION_DEFAULT_SPLIT_DISMISS;
- }
- StateAnimationConfig config = new StateAnimationConfig();
- config.duration = duration;
- AnimatorSet stateAnim = stateManager.createAtomicAnimation(
- startState, NORMAL, config);
- AnimatorSet dismissAnim = splitSelectController.getSplitAnimationController()
- .createPlaceholderDismissAnim(mContainer,
- LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON, duration);
- stateAnim.play(dismissAnim);
- stateManager.setCurrentAnimation(stateAnim, NORMAL);
- stateAnim.start();
+
+ AnimUtils.goToNormalStateWithSplitDismissal(stateManager, mContainer,
+ LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON,
+ splitSelectController.getSplitAnimationController());
}
void ensureProperRotation() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index e7a8720..af32ba2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -34,7 +34,6 @@
import com.android.quickstep.recents.di.getScope
import com.android.quickstep.recents.di.inject
import com.android.quickstep.recents.viewmodel.TaskContainerViewModel
-import com.android.quickstep.task.thumbnail.TaskThumbnail
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
@@ -69,7 +68,8 @@
private val taskContainerViewModel: TaskContainerViewModel by lazy {
TaskContainerViewModel(
sysUiStatusNavFlagsUseCase = RecentsDependencies.get(),
- getThumbnailUseCase = RecentsDependencies.get()
+ getThumbnailUseCase = RecentsDependencies.get(),
+ splashAlphaUseCase = RecentsDependencies.get(),
)
}
@@ -81,7 +81,7 @@
val taskViewScope = RecentsDependencies.getScope(taskView)
linkTo(taskViewScope)
- val taskContainerScope = RecentsDependencies.getScope(this)
+ val taskContainerScope = RecentsDependencies.getScope(this@TaskContainer)
linkTo(taskContainerScope)
}
} else {
@@ -112,10 +112,10 @@
// TODO(b/334826842): Support shouldShowSplashView for new TTV.
val shouldShowSplashView: Boolean
get() =
- if (enableRefactorTaskThumbnail()) false
+ if (enableRefactorTaskThumbnail())
+ taskContainerViewModel.shouldShowThumbnailSplash(task.key.id)
else thumbnailViewDeprecated.shouldShowSplashView()
- // TODO(b/350743460) Support sysUiStatusNavFlags for new TTV.
val sysUiStatusNavFlags: Int
get() =
if (enableRefactorTaskThumbnail())
@@ -163,9 +163,7 @@
}
fun bindThumbnailView() {
- // TODO(b/343364498): Existing view has shouldShowScreenshot as an override as well but
- // this should be decided inside TaskThumbnailViewModel.
- taskThumbnailViewModel.bind(TaskThumbnail(task.key.id, taskView.isRunningTask))
+ taskThumbnailViewModel.bind(task.key.id)
}
fun setOverlayEnabled(enabled: Boolean) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index f2f036a..e189d14 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -123,6 +123,10 @@
/** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
get() = taskContainers.map { it.task.key.id }.toIntArray()
+ val taskIdSet: Set<Int>
+ /** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
+ get() = taskContainers.map { it.task.key.id }.toSet()
+
val snapshotViews: Array<View>
get() = taskContainers.map { it.snapshotView }.toTypedArray()
@@ -285,6 +289,9 @@
set(value) {
field = value
applyScale()
+ if (enableRefactorTaskThumbnail()) {
+ taskViewModel.updateNonGridScale(value)
+ }
}
private var dismissScale = 1f
@@ -566,9 +573,7 @@
resetPersistentViewTransforms()
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
- if (enableRefactorTaskThumbnail()) {
- notifyIsRunningTaskUpdated()
- } else {
+ if (!enableRefactorTaskThumbnail()) {
taskContainers.forEach { it.thumbnailViewDeprecated.setThumbnail(it.task, null) }
}
setOverlayEnabled(false)
@@ -918,9 +923,8 @@
iconView.setText(text)
}
- open fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
+ open fun refreshThumbnails(thumbnailDatas: Map<Int, ThumbnailData?>?) {
if (enableRefactorTaskThumbnail()) {
- // TODO(b/342560598) add thumbnail logic
return
}
@@ -1051,11 +1055,9 @@
if (isQuickSwitch) {
setFreezeRecentTasksReordering()
}
- // TODO(b/334826842) add splash functionality to new TTV
- if (!enableRefactorTaskThumbnail()) {
- disableStartingWindow =
- firstContainer.thumbnailViewDeprecated.shouldShowSplashView()
- }
+ // TODO(b/334826842) no work required - add splash functionality to new TTV -
+ // cold start e.g. restart device. Small splash moving to bigger splash
+ disableStartingWindow = firstContainer.shouldShowSplashView
}
Executors.UI_HELPER_EXECUTOR.execute {
if (
@@ -1396,7 +1398,6 @@
protected open fun refreshTaskThumbnailSplash() {
if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334826842) add splash functionality to new TTV
taskContainers.forEach { it.thumbnailViewDeprecated.refreshSplashView() }
}
}
@@ -1420,7 +1421,6 @@
protected open fun applyThumbnailSplashAlpha() {
if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334826842) add splash functionality to new TTV
taskContainers.forEach {
it.thumbnailViewDeprecated.setSplashAlpha(taskThumbnailSplashAlpha)
}
@@ -1490,11 +1490,6 @@
}
}
- /** Updates [TaskThumbnailView] to reflect the latest [Task] state (i.e., task isRunning). */
- fun notifyIsRunningTaskUpdated() {
- taskContainers.forEach { it.bindThumbnailView() }
- }
-
fun resetPersistentViewTransforms() {
nonGridTranslationX = 0f
gridTranslationX = 0f
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt
new file mode 100644
index 0000000..3e0d6b5
--- /dev/null
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewScreenshotTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.util.PathParser
+import android.view.LayoutInflater
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.R
+import com.android.wm.shell.common.bubbles.BubbleInfo
+import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** Screenshot tests for [BubbleView]. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class BubbleViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() =
+ DeviceEmulationSpec.forDisplays(
+ Displays.Phone,
+ isDarkTheme = false,
+ isLandscape = false
+ )
+ }
+
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec))
+ )
+
+ @Test
+ fun bubbleView_hasUnseenContent() {
+ screenshotRule.screenshotTest("bubbleView_hasUnseenContent") { activity ->
+ activity.actionBar?.hide()
+ setupBubbleView()
+ }
+ }
+
+ @Test
+ fun bubbleView_seen() {
+ screenshotRule.screenshotTest("bubbleView_seen") { activity ->
+ activity.actionBar?.hide()
+ setupBubbleView().apply { markSeen() }
+ }
+ }
+
+ @Test
+ fun bubbleView_badgeHidden() {
+ screenshotRule.screenshotTest("bubbleView_badgeHidden") { activity ->
+ activity.actionBar?.hide()
+ setupBubbleView().apply { setBadgeScale(0f) }
+ }
+ }
+
+ private fun setupBubbleView(): BubbleView {
+ val inflater = LayoutInflater.from(context)
+
+ val iconSize = 100
+ // BubbleView uses launcher's badge to icon ratio and expects the badge image to already
+ // have the right size
+ val badgeToIconRatio = 0.444f
+ val badgeRadius = iconSize * badgeToIconRatio / 2
+ val icon = createCircleBitmap(radius = iconSize / 2, color = Color.LTGRAY)
+ val badge = createCircleBitmap(radius = badgeRadius.toInt(), color = Color.RED)
+
+ val bubbleInfo = BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false)
+ val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null) as BubbleView
+ val dotPath =
+ PathParser.createPathFromPathData(
+ context.resources.getString(com.android.internal.R.string.config_icon_mask)
+ )
+ val bubble =
+ BubbleBarBubble(bubbleInfo, bubbleView, badge, icon, Color.BLUE, dotPath, "test app")
+ bubbleView.setBubble(bubble)
+ bubbleView.showDotIfNeeded(1f)
+ return bubbleView
+ }
+
+ private fun createCircleBitmap(radius: Int, color: Int): Bitmap {
+ val bitmap = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ canvas.drawARGB(0, 0, 0, 0)
+ val paint = Paint()
+ paint.color = color
+ canvas.drawCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), paint)
+ return bitmap
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
similarity index 79%
rename from quickstep/tests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
index a532762..0005df6 100644
--- a/quickstep/tests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
@@ -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 android.app.prediction.AppPredictor
@@ -19,7 +34,7 @@
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
/** Unit tests for [QuickstepModelDelegate]. */
@@ -57,25 +72,25 @@
underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_PREDICTION)
verify(allAppsPredictor).notifyAppTargetEvent(mockedAppTargetEvent)
- verifyZeroInteractions(hotseatPredictor)
- verifyZeroInteractions(widgetRecommendationPredictor)
+ verifyNoMoreInteractions(hotseatPredictor)
+ verifyNoMoreInteractions(widgetRecommendationPredictor)
}
@Test
fun onWidgetPrediction_notifyWidgetRecommendationPredictor() {
underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_WIDGETS_PREDICTION)
- verifyZeroInteractions(allAppsPredictor)
+ verifyNoMoreInteractions(allAppsPredictor)
verify(widgetRecommendationPredictor).notifyAppTargetEvent(mockedAppTargetEvent)
- verifyZeroInteractions(hotseatPredictor)
+ verifyNoMoreInteractions(hotseatPredictor)
}
@Test
fun onHotseatPrediction_notifyHotseatPredictor() {
underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_HOTSEAT_PREDICTION)
- verifyZeroInteractions(allAppsPredictor)
- verifyZeroInteractions(widgetRecommendationPredictor)
+ verifyNoMoreInteractions(allAppsPredictor)
+ verifyNoMoreInteractions(widgetRecommendationPredictor)
verify(hotseatPredictor).notifyAppTargetEvent(mockedAppTargetEvent)
}
@@ -83,8 +98,8 @@
fun onOtherClient_notifyHotseatPredictor() {
underTest.onAppTargetEvent(mockedAppTargetEvent, CONTAINER_WALLPAPERS)
- verifyZeroInteractions(allAppsPredictor)
- verifyZeroInteractions(widgetRecommendationPredictor)
+ verifyNoMoreInteractions(allAppsPredictor)
+ verifyNoMoreInteractions(widgetRecommendationPredictor)
verify(hotseatPredictor).notifyAppTargetEvent(mockedAppTargetEvent)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
new file mode 100644
index 0000000..a57fb70
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarControllerTestUtil.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.getInstrumentation
+
+object TaskbarControllerTestUtil {
+ inline fun runOnMainSync(crossinline runTest: () -> Unit) {
+ getInstrumentation().runOnMainSync { runTest() }
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt
new file mode 100644
index 0000000..e583f63
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarEduTooltipControllerTest.kt
@@ -0,0 +1,205 @@
+/*
+ * 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.Utilities
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
+import com.android.launcher3.taskbar.rules.TaskbarModeRule
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
+import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.taskbar.rules.TaskbarPinningPreferenceRule
+import com.android.launcher3.taskbar.rules.TaskbarPreferenceRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.launcher3.util.OnboardingPrefs
+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(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+class TaskbarEduTooltipControllerTest {
+
+ private val context =
+ TaskbarWindowSandboxContext.create(
+ InstrumentationRegistry.getInstrumentation().targetContext
+ )
+
+ @get:Rule
+ val tooltipStepPreferenceRule =
+ TaskbarPreferenceRule(
+ context,
+ OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.prefItem,
+ )
+
+ @get:Rule
+ val searchEduPreferenceRule =
+ TaskbarPreferenceRule(
+ context,
+ OnboardingPrefs.TASKBAR_SEARCH_EDU_SEEN,
+ )
+
+ @get:Rule val taskbarPinningPreferenceRule = TaskbarPinningPreferenceRule(context)
+
+ @get:Rule val taskbarModeRule = TaskbarModeRule(context)
+
+ @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
+
+ private val taskbarContext: TaskbarActivityContext
+ get() = taskbarUnitTestRule.activityContext
+
+ private val wasInTestHarness = Utilities.isRunningInTestHarness()
+
+ @Before
+ fun setUp() {
+ Utilities.disableRunningInTestHarnessForTests()
+ }
+
+ @After
+ fun tearDown() {
+ if (wasInTestHarness) {
+ Utilities.enableRunningInTestHarnessForTests()
+ }
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testMaybeShowSwipeEdu_whenTaskbarIsInThreeButtonMode_doesNotShowSwipeEdu() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
+ runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testMaybeShowSwipeEdu_whenSwipeEduAlreadyShown_doesNotShowSwipeEdu() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_FEATURES
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
+ runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testMaybeShowSwipeEdu_whenUserHasNotSeen_doesShowSwipeEdu() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
+ runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testMaybeShowFeaturesEdu_whenFeatureEduAlreadyShown_doesNotShowFeatureEdu() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_NONE
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
+ runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testMaybeShowFeaturesEdu_whenUserHasNotSeen_doesShowFeatureEdu() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_FEATURES
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
+ runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(THREE_BUTTONS)
+ fun testMaybeShowPinningEdu_whenTaskbarIsInThreeButtonMode_doesNotShowPinningEdu() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_PINNING
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
+ runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testMaybeShowPinningEdu_whenUserHasNotSeen_doesShowPinningEdu() {
+ // Test standalone pinning edu, where user has seen taskbar edu before, but not pinning edu.
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_PINNING
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
+ runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testIsBeforeTooltipFeaturesStep_whenUserHasNotSeenFeatureEdu_shouldReturnTrue() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ assertThat(taskbarEduTooltipController.isBeforeTooltipFeaturesStep).isTrue()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testIsBeforeTooltipFeaturesStep_whenUserHasSeenFeatureEdu_shouldReturnFalse() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_NONE
+ assertThat(taskbarEduTooltipController.isBeforeTooltipFeaturesStep).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testHide_whenTooltipIsOpen_shouldCloseTooltip() {
+ tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
+ assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
+ runOnMainSync { taskbarEduTooltipController.hide() }
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(TRANSIENT)
+ fun testMaybeShowSearchEdu_whenTaskbarIsTransient_shouldNotShowSearchEdu() {
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ runOnMainSync { taskbarEduTooltipController.init(taskbarContext.controllers) }
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+
+ @Test
+ @TaskbarMode(PINNED)
+ fun testMaybeShowSearchEdu_whenTaskbarIsPinnedAndUserHasSeenSearchEdu_shouldNotShowSearchEdu() {
+ searchEduPreferenceRule.value = true
+ assertThat(taskbarEduTooltipController.userHasSeenSearchEdu).isTrue()
+ runOnMainSync { taskbarEduTooltipController.hide() }
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ runOnMainSync { taskbarEduTooltipController.init(taskbarContext.controllers) }
+ assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
index 2f0b446..43d924a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
@@ -26,6 +26,7 @@
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.notification.NotificationKeyData
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
@@ -56,13 +57,13 @@
@Test
fun testToggle_once_showsAllApps() {
- getInstrumentation().runOnMainSync { allAppsController.toggle() }
+ runOnMainSync { allAppsController.toggle() }
assertThat(allAppsController.isOpen).isTrue()
}
@Test
fun testToggle_twice_closesAllApps() {
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
allAppsController.toggle()
allAppsController.toggle()
}
@@ -71,7 +72,7 @@
@Test
fun testToggle_taskbarRecreated_allAppsReopened() {
- getInstrumentation().runOnMainSync { allAppsController.toggle() }
+ runOnMainSync { allAppsController.toggle() }
taskbarUnitTestRule.recreateTaskbar()
assertThat(allAppsController.isOpen).isTrue()
}
@@ -138,7 +139,7 @@
@Test
fun testUpdateNotificationDots_appInfo_hasDot() {
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
allAppsController.setApps(TEST_APPS, 0, emptyMap())
allAppsController.toggle()
taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
@@ -162,7 +163,7 @@
@Test
fun testUpdateNotificationDots_predictedApp_hasDot() {
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
allAppsController.toggle()
taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
@@ -185,12 +186,12 @@
@Test
fun testToggleSearch_searchEditTextFocused() {
- getInstrumentation().runOnMainSync { allAppsController.toggleSearch() }
- getInstrumentation().runOnMainSync {
+ runOnMainSync { allAppsController.toggleSearch() }
+ runOnMainSync {
// All Apps is now attached to window. Open animation is posted but not started.
}
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
// Animation has started. Advance to end of animation.
animatorTestRule.advanceTimeBy(overlayController.openDuration.toLong())
}
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 e9c0dd6..619ce1c 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
@@ -35,8 +35,8 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
import com.android.launcher3.taskbar.bubbles.BubbleBarOverflow
import com.android.launcher3.taskbar.bubbles.BubbleBarView
-import com.android.launcher3.taskbar.bubbles.BubbleStashController
import com.android.launcher3.taskbar.bubbles.BubbleView
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.wm.shell.common.bubbles.BubbleInfo
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
@@ -78,7 +78,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
@@ -122,7 +122,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
@@ -165,7 +165,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
@@ -205,7 +205,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
@@ -246,7 +246,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val animator =
BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
@@ -278,7 +278,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -321,7 +321,7 @@
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
- whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator)
+ whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
@@ -451,9 +451,9 @@
private fun setUpBubbleStashController() {
bubbleStashController = mock<BubbleStashController>()
whenever(bubbleStashController.isStashed).thenReturn(true)
- whenever(bubbleStashController.diffBetweenHandleAndBarCenters)
+ whenever(bubbleStashController.getDiffBetweenHandleAndBarCenters())
.thenReturn(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS)
- whenever(bubbleStashController.stashedHandleTranslationForNewBubbleAnimation)
+ whenever(bubbleStashController.getStashedHandleTranslationForNewBubbleAnimation())
.thenReturn(HANDLE_TRANSLATION)
whenever(bubbleStashController.bubbleBarTranslationYForTaskbar)
.thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
new file mode 100644
index 0000000..c0a5dfa
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
@@ -0,0 +1,258 @@
+/*
+ * 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.stashing
+
+import android.animation.AnimatorTestRule
+import android.content.Context
+import android.widget.FrameLayout
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.anim.AnimatedFloat
+import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.util.MultiValueAlpha
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** Unit tests for [PersistentBubbleStashController]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PersistentBubbleStashControllerTest {
+
+ companion object {
+ const val BUBBLE_BAR_HEIGHT = 100f
+ const val HOTSEAT_TRANSLATION_Y = -45f
+ const val TASK_BAR_TRANSLATION_Y = -5f
+ }
+
+ @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
+
+ @get:Rule val rule: MockitoRule = MockitoJUnit.rule()
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var bubbleBarView: BubbleBarView
+
+ @Mock lateinit var bubbleBarViewController: BubbleBarViewController
+
+ @Mock lateinit var taskbarInsetsController: TaskbarInsetsController
+
+ private lateinit var persistentTaskBarStashController: PersistentBubbleStashController
+ private lateinit var translationY: AnimatedFloat
+ private lateinit var scale: AnimatedFloat
+ private lateinit var alpha: MultiValueAlpha
+
+ @Before
+ fun setUp() {
+ persistentTaskBarStashController =
+ PersistentBubbleStashController(DefaultDimensionsProvider())
+ setUpBubbleBarView()
+ setUpBubbleBarController()
+ persistentTaskBarStashController.init(
+ taskbarInsetsController,
+ bubbleBarViewController,
+ null,
+ ImmediateAction()
+ )
+ }
+
+ @Test
+ fun setBubblesShowingOnHomeUpdatedToFalse_barPositionYUpdated_controllersNotified() {
+ // Given bubble bar is on home and has bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.isBubblesShowingOnHome = true
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ // When switch out of the home screen
+ getInstrumentation().runOnMainSync {
+ persistentTaskBarStashController.isBubblesShowingOnHome = false
+ }
+
+ // Then translation Y is animating and the bubble bar controller is notified
+ assertThat(translationY.isAnimating).isTrue()
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
+ // Wait until animation ends
+ advanceTimeBy(BubbleStashController.BAR_TRANSLATION_DURATION)
+ // Check translation Y is correct and the insets controller is notified
+ assertThat(bubbleBarView.translationY).isEqualTo(TASK_BAR_TRANSLATION_Y)
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun setBubblesShowingOnHomeUpdatedToTrue_barPositionYUpdated_controllersNotified() {
+ // Given bubble bar has bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ // When switch to home screen
+ getInstrumentation().runOnMainSync {
+ persistentTaskBarStashController.isBubblesShowingOnHome = true
+ }
+
+ // Then translation Y is animating and the bubble bar controller is notified
+ assertThat(translationY.isAnimating).isTrue()
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
+ // Wait until animation ends
+ advanceTimeBy(BubbleStashController.BAR_TRANSLATION_DURATION)
+
+ // Check translation Y is correct and the insets controller is notified
+ assertThat(bubbleBarView.translationY).isEqualTo(HOTSEAT_TRANSLATION_Y)
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun setBubblesShowingOnOverviewUpdatedToFalse_controllersNotified() {
+ // Given bubble bar is on overview
+ persistentTaskBarStashController.isBubblesShowingOnOverview = true
+ clearInvocations(bubbleBarViewController)
+
+ // When switch out of the overview screen
+ persistentTaskBarStashController.isBubblesShowingOnOverview = false
+
+ // Then bubble bar controller is notified
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
+ }
+
+ @Test
+ fun setBubblesShowingOnOverviewUpdatedToTrue_controllersNotified() {
+ // When switch to the overview screen
+ persistentTaskBarStashController.isBubblesShowingOnOverview = true
+
+ // Then bubble bar controller is notified
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
+ }
+
+ @Test
+ fun isSysuiLockedSwitchedToFalseForOverview_unlockAnimationIsShown() {
+ // Given screen is locked and bubble bar has bubbles
+ persistentTaskBarStashController.isSysuiLocked = true
+ persistentTaskBarStashController.isBubblesShowingOnOverview = true
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ // When switch to the overview screen
+ getInstrumentation().runOnMainSync {
+ persistentTaskBarStashController.isSysuiLocked = false
+ }
+
+ // Then
+ assertThat(translationY.isAnimating).isTrue()
+ assertThat(scale.isAnimating).isTrue()
+ // Wait until animation ends
+ advanceTimeBy(BubbleStashController.BAR_STASH_DURATION)
+
+ // Then bubble bar is fully visible at the correct location
+ assertThat(bubbleBarView.scaleX).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1f)
+ assertThat(bubbleBarView.translationY).isEqualTo(TASK_BAR_TRANSLATION_Y)
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ // Insets controller is notified
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun showBubbleBarImmediateToY() {
+ // Given bubble bar is fully transparent and scaled to 0 at 0 y position
+ val targetY = 341f
+ bubbleBarView.alpha = 0f
+ bubbleBarView.scaleX = 0f
+ bubbleBarView.scaleY = 0f
+ bubbleBarView.translationY = 0f
+
+ // When
+ persistentTaskBarStashController.showBubbleBarImmediate(targetY)
+
+ // Then all property values are updated
+ assertThat(bubbleBarView.translationY).isEqualTo(targetY)
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleX).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1f)
+ }
+
+ @Test
+ fun isTransientTaskbar_false() {
+ assertThat(persistentTaskBarStashController.isTransientTaskBar).isFalse()
+ }
+
+ @Test
+ fun hasHandleView_false() {
+ assertThat(persistentTaskBarStashController.hasHandleView).isFalse()
+ }
+
+ @Test
+ fun isStashed_false() {
+ assertThat(persistentTaskBarStashController.isStashed).isFalse()
+ }
+
+ @Test
+ fun bubbleBarTranslationYForTaskbar() {
+ // Give bubble bar is on home
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
+ persistentTaskBarStashController.isBubblesShowingOnHome = true
+
+ // Then bubbleBarTranslationY would be HOTSEAT_TRANSLATION_Y
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isEqualTo(HOTSEAT_TRANSLATION_Y)
+
+ // Give bubble bar is not on home
+ persistentTaskBarStashController.isBubblesShowingOnHome = false
+
+ // Then bubbleBarTranslationY would be TASK_BAR_TRANSLATION_Y
+ assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
+ .isEqualTo(TASK_BAR_TRANSLATION_Y)
+ }
+
+ private fun advanceTimeBy(advanceMs: Long) {
+ // Advance animator for on-device tests
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(advanceMs) }
+ }
+
+ private fun setUpBubbleBarView() {
+ getInstrumentation().runOnMainSync {
+ bubbleBarView = BubbleBarView(context)
+ bubbleBarView.layoutParams = FrameLayout.LayoutParams(0, 0)
+ }
+ }
+
+ private fun setUpBubbleBarController() {
+ translationY = AnimatedFloat(Runnable { bubbleBarView.translationY = translationY.value })
+ scale =
+ AnimatedFloat(
+ Runnable {
+ val scale: Float = scale.value
+ bubbleBarView.scaleX = scale
+ bubbleBarView.scaleY = scale
+ }
+ )
+ alpha = MultiValueAlpha(bubbleBarView, 1 /* num alpha channels */)
+
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+ whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(translationY)
+ whenever(bubbleBarViewController.bubbleBarScale).thenReturn(scale)
+ whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(alpha)
+ whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt
new file mode 100644
index 0000000..0f8a2c3
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/StashingTestUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.stashing
+
+class ImmediateAction : BubbleStashController.ControllersAfterInitAction {
+ override fun runAfterInit(action: Runnable) = action.run()
+}
+
+class DefaultDimensionsProvider(
+ private val taskBarBottomSpace: Int = TASKBAR_BOTTOM_SPACE,
+ private val taskBarHeight: Int = TASKBAR_HEIGHT,
+ private val hotseatBottomSpace: Int = HOTSEAT_BOTTOM_SPACE,
+ private val hotseatHeight: Int = HOTSEAT_HEIGHT
+) : BubbleStashController.TaskbarHotseatDimensionsProvider {
+ override fun getTaskbarBottomSpace(): Int = taskBarBottomSpace
+
+ override fun getTaskbarHeight(): Int = taskBarHeight
+
+ override fun getHotseatBottomSpace(): Int = hotseatBottomSpace
+
+ override fun getHotseatHeight(): Int = hotseatHeight
+
+ companion object {
+ const val TASKBAR_BOTTOM_SPACE = 0
+ const val TASKBAR_HEIGHT = 110
+ const val HOTSEAT_BOTTOM_SPACE = 20
+ const val HOTSEAT_HEIGHT = 150
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
new file mode 100644
index 0000000..b5809c2
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -0,0 +1,336 @@
+/*
+ * 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.stashing
+
+import android.animation.AnimatorTestRule
+import android.content.Context
+import android.view.View
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.anim.AnimatedFloat
+import com.android.launcher3.taskbar.StashedHandleView
+import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.bubbles.BubbleBarView
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE
+import com.android.launcher3.util.MultiValueAlpha
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** Unit tests for [TransientBubbleStashController]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TransientBubbleStashControllerTest {
+
+ companion object {
+ const val TASKBAR_BOTTOM_SPACE = 5
+ const val BUBBLE_BAR_HEIGHT = 100f
+ const val HOTSEAT_TRANSLATION_Y = -45f
+ const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE
+ const val HANDLE_VIEW_HEIGHT = 4
+ const val BUBBLE_BAR_STASHED_TRANSLATION_Y = 48
+ }
+
+ @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
+
+ @get:Rule val rule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock lateinit var bubbleStashedHandleViewController: BubbleStashedHandleViewController
+
+ @Mock lateinit var bubbleBarViewController: BubbleBarViewController
+
+ @Mock lateinit var taskbarInsetsController: TaskbarInsetsController
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var bubbleBarView: BubbleBarView
+ private lateinit var stashedHandleView: StashedHandleView
+ private lateinit var barTranslationY: AnimatedFloat
+ private lateinit var barScale: AnimatedFloat
+ private lateinit var barAlpha: MultiValueAlpha
+ private lateinit var stashedHandleAlpha: MultiValueAlpha
+ private lateinit var stashedHandleScale: AnimatedFloat
+ private lateinit var stashedHandleTranslationY: AnimatedFloat
+ private lateinit var stashPhysicsAnimator: PhysicsAnimator<View>
+
+ private lateinit var mTransientBubbleStashController: TransientBubbleStashController
+
+ @Before
+ fun setUp() {
+ val taskbarHotseatDimensionsProvider =
+ DefaultDimensionsProvider(taskBarBottomSpace = TASKBAR_BOTTOM_SPACE)
+ mTransientBubbleStashController =
+ TransientBubbleStashController(taskbarHotseatDimensionsProvider, context.resources)
+ setUpBubbleBarView()
+ setUpBubbleBarController()
+ setUpStashedHandleView()
+ setUpBubbleStashedHandleViewController()
+ PhysicsAnimatorTestUtils.prepareForTest()
+ mTransientBubbleStashController.init(
+ taskbarInsetsController,
+ bubbleBarViewController,
+ bubbleStashedHandleViewController,
+ ImmediateAction()
+ )
+ }
+
+ @Test
+ fun setBubblesShowingOnHomeUpdatedToTrue_barPositionYUpdated_controllersNotified() {
+ // Given bubble bar is on home and has bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ // When switch out of the home screen
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.isBubblesShowingOnHome = true
+ }
+
+ // Then BubbleBarView is animating, BubbleBarViewController controller is notified
+ assertThat(barTranslationY.isAnimating).isTrue()
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
+
+ // Wait until animation ends
+ advanceTimeBy(BubbleStashController.BAR_TRANSLATION_DURATION)
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+ // Then translation Y is correct and the insets controller is notified
+ assertThat(barTranslationY.isAnimating).isFalse()
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ assertThat(bubbleBarView.translationY).isEqualTo(HOTSEAT_TRANSLATION_Y)
+ }
+
+ @Test
+ fun setBubblesShowingOnOverviewUpdatedToTrue_barPositionYUpdated_controllersNotified() {
+ // Given bubble bar is on home and has bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ // When switch out of the home screen
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.isBubblesShowingOnOverview = true
+ }
+
+ // Then BubbleBarView is animating, BubbleBarViewController controller is notified
+ assertThat(barTranslationY.isAnimating).isTrue()
+ verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
+
+ // Wait until animation ends
+ advanceTimeBy(BubbleStashController.BAR_TRANSLATION_DURATION)
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+ // Then translation Y is correct and the insets controller is notified
+ assertThat(barTranslationY.isAnimating).isFalse()
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ assertThat(bubbleBarView.translationY).isEqualTo(TASK_BAR_TRANSLATION_Y)
+ }
+
+ @Test
+ fun updateStashedAndExpandedState_stashAndCollapse_bubbleBarHidden_stashedHandleShown() {
+ // Given bubble bar has bubbles and not stashed
+ mTransientBubbleStashController.isStashed = false
+ whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false)
+
+ // When stash
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.updateStashedAndExpandedState(
+ stash = true,
+ expand = false
+ )
+ }
+
+ // Wait until animations ends
+ advanceTimeBy(BubbleStashController.BAR_STASH_DURATION)
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+ // Then check BubbleBarController is notified
+ verify(bubbleBarViewController).onStashStateChanging()
+ // Bubble bar is stashed
+ assertThat(mTransientBubbleStashController.isStashed).isTrue()
+ assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y)
+ assertThat(bubbleBarView.alpha).isEqualTo(0f)
+ assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE)
+ assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE)
+ // Handle view is visible
+ assertThat(stashedHandleView.translationY).isEqualTo(0)
+ assertThat(stashedHandleView.alpha).isEqualTo(1)
+ }
+
+ @Test
+ fun isSysuiLockedSwitchedToFalseForOverview_unlockAnimationIsShown() {
+ // Given screen is locked and bubble bar has bubbles
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.isSysuiLocked = true
+ mTransientBubbleStashController.isBubblesShowingOnOverview = true
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+ }
+ advanceTimeBy(BubbleStashController.BAR_TRANSLATION_DURATION)
+
+ // When switch to the overview screen
+ getInstrumentation().runOnMainSync { mTransientBubbleStashController.isSysuiLocked = false }
+
+ // Then
+ assertThat(barTranslationY.isAnimating).isTrue()
+ assertThat(barScale.isAnimating).isTrue()
+ // Wait until animation ends
+ advanceTimeBy(BubbleStashController.BAR_STASH_DURATION)
+
+ // Then bubble bar is fully visible at the correct location
+ assertThat(bubbleBarView.scaleX).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1f)
+ assertThat(bubbleBarView.translationY)
+ .isEqualTo(PersistentBubbleStashControllerTest.TASK_BAR_TRANSLATION_Y)
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ // Insets controller is notified
+ verify(taskbarInsetsController, atLeastOnce())
+ .onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun showBubbleBarImmediateToY() {
+ // Given bubble bar is fully transparent and scaled to 0 at 0 y position
+ val targetY = 341f
+ bubbleBarView.alpha = 0f
+ bubbleBarView.scaleX = 0f
+ bubbleBarView.scaleY = 0f
+ bubbleBarView.translationY = 0f
+ stashedHandleView.translationY = targetY
+
+ // When
+ mTransientBubbleStashController.showBubbleBarImmediate(targetY)
+
+ // Then all property values are updated
+ assertThat(bubbleBarView.translationY).isEqualTo(targetY)
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleX).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1f)
+ // Handle is transparent
+ assertThat(stashedHandleView.alpha).isEqualTo(0)
+ // Insets controller is notified
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun stashBubbleBarImmediate() {
+ // When
+ mTransientBubbleStashController.stashBubbleBarImmediate()
+
+ // Then all property values are updated
+ assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y)
+ assertThat(bubbleBarView.alpha).isEqualTo(0)
+ assertThat(bubbleBarView.scaleX).isEqualTo(STASHED_BAR_SCALE)
+ assertThat(bubbleBarView.scaleY).isEqualTo(STASHED_BAR_SCALE)
+ // Handle is visible at correct Y position
+ assertThat(stashedHandleView.alpha).isEqualTo(1)
+ assertThat(stashedHandleView.translationY).isEqualTo(0)
+ // Insets controller is notified
+ verify(taskbarInsetsController).onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ }
+
+ @Test
+ fun getTouchableHeight_stashed_stashHeightReturned() {
+ // When
+ mTransientBubbleStashController.isStashed = true
+ val height = mTransientBubbleStashController.getTouchableHeight()
+
+ // Then
+ assertThat(height).isEqualTo(HANDLE_VIEW_HEIGHT)
+ }
+
+ @Test
+ fun getTouchableHeight_unstashed_barHeightReturned() {
+ // When BubbleBar is not stashed
+ mTransientBubbleStashController.isStashed = false
+ val height = mTransientBubbleStashController.getTouchableHeight()
+
+ // Then bubble bar height is returned
+ assertThat(height).isEqualTo(BUBBLE_BAR_HEIGHT.toInt())
+ }
+
+ private fun advanceTimeBy(advanceMs: Long) {
+ // Advance animator for on-device tests
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(advanceMs) }
+ }
+
+ private fun setUpBubbleBarView() {
+ getInstrumentation().runOnMainSync {
+ bubbleBarView = BubbleBarView(context)
+ bubbleBarView.layoutParams = FrameLayout.LayoutParams(0, 0)
+ }
+ }
+
+ private fun setUpStashedHandleView() {
+ getInstrumentation().runOnMainSync {
+ stashedHandleView = StashedHandleView(context)
+ stashedHandleView.layoutParams = FrameLayout.LayoutParams(0, 0)
+ }
+ }
+
+ private fun setUpBubbleBarController() {
+ barTranslationY =
+ AnimatedFloat(Runnable { bubbleBarView.translationY = barTranslationY.value })
+ barScale =
+ AnimatedFloat(
+ Runnable {
+ val scale: Float = barScale.value
+ bubbleBarView.scaleX = scale
+ bubbleBarView.scaleY = scale
+ }
+ )
+ barAlpha = MultiValueAlpha(bubbleBarView, 1 /* num alpha channels */)
+
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+ whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY)
+ whenever(bubbleBarViewController.bubbleBarScale).thenReturn(barScale)
+ whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha)
+ whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT)
+ }
+
+ private fun setUpBubbleStashedHandleViewController() {
+ stashedHandleTranslationY =
+ AnimatedFloat(Runnable { stashedHandleView.translationY = barTranslationY.value })
+ stashedHandleScale =
+ AnimatedFloat(
+ Runnable {
+ val scale: Float = barScale.value
+ bubbleBarView.scaleX = scale
+ bubbleBarView.scaleY = scale
+ }
+ )
+ stashedHandleAlpha = MultiValueAlpha(stashedHandleView, 1 /* num alpha channels */)
+ stashPhysicsAnimator = PhysicsAnimator.getInstance(stashedHandleView)
+ whenever(bubbleStashedHandleViewController.stashedHandleAlpha)
+ .thenReturn(stashedHandleAlpha)
+ whenever(bubbleStashedHandleViewController.physicsAnimator).thenReturn(stashPhysicsAnimator)
+ whenever(bubbleStashedHandleViewController.stashedHeight).thenReturn(HANDLE_VIEW_HEIGHT)
+ whenever(bubbleStashedHandleViewController.setTranslationYForSwipe(any())).thenAnswer {
+ invocation ->
+ (invocation.arguments[0] as Float).also { stashedHandleView.translationY = it }
+ }
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
index f946d4d..4fa821d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
@@ -25,6 +25,7 @@
import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
import com.android.launcher3.AbstractFloatingView.hasOpenView
import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
@@ -64,7 +65,7 @@
@Test
fun testRequestWindow_afterHidingExistingWindow_createsNewWindow() {
val context1 = getOnUiThread { overlayController.requestWindow() }
- getInstrumentation().runOnMainSync { overlayController.hideWindow() }
+ runOnMainSync { overlayController.hideWindow() }
val context2 = getOnUiThread { overlayController.requestWindow() }
assertThat(context1).isNotSameInstanceAs(context2)
@@ -73,7 +74,7 @@
@Test
fun testRequestWindow_afterHidingOverlay_createsNewWindow() {
val context1 = getOnUiThread { overlayController.requestWindow() }
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
TestOverlayView.show(context1)
overlayController.hideWindow()
}
@@ -84,16 +85,14 @@
@Test
fun testRequestWindow_addsProxyView() {
- getInstrumentation().runOnMainSync {
- TestOverlayView.show(overlayController.requestWindow())
- }
+ runOnMainSync { TestOverlayView.show(overlayController.requestWindow()) }
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
}
@Test
fun testRequestWindow_closeProxyView_closesOverlay() {
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
AbstractFloatingView.closeOpenContainer(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)
}
assertThat(overlay.isOpen).isFalse()
@@ -103,13 +102,13 @@
fun testRequestWindow_attachesDragLayer() {
val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
// Allow drag layer to attach before checking.
- getInstrumentation().runOnMainSync { assertThat(dragLayer.isAttachedToWindow).isTrue() }
+ runOnMainSync { assertThat(dragLayer.isAttachedToWindow).isTrue() }
}
@Test
fun testHideWindow_closesOverlay() {
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
- getInstrumentation().runOnMainSync { overlayController.hideWindow() }
+ runOnMainSync { overlayController.hideWindow() }
assertThat(overlay.isOpen).isFalse()
}
@@ -118,7 +117,7 @@
val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
// Wait for drag layer to be attached to window before hiding.
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
overlayController.hideWindow()
assertThat(dragLayer.isAttachedToWindow).isFalse()
}
@@ -132,7 +131,7 @@
Pair(TestOverlayView.show(context), TestOverlayView.show(context))
}
- getInstrumentation().runOnMainSync { overlay1.close(false) }
+ runOnMainSync { overlay1.close(false) }
assertThat(overlay2.isOpen).isTrue()
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
}
@@ -145,7 +144,7 @@
Pair(TestOverlayView.show(context), TestOverlayView.show(context))
}
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
overlay1.close(false)
overlay2.close(false)
}
@@ -154,9 +153,7 @@
@Test
fun testRecreateTaskbar_closesWindow() {
- getInstrumentation().runOnMainSync {
- TestOverlayView.show(overlayController.requestWindow())
- }
+ runOnMainSync { TestOverlayView.show(overlayController.requestWindow()) }
taskbarUnitTestRule.recreateTaskbar()
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isFalse()
}
@@ -166,29 +163,25 @@
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
TaskStackChangeListeners.getInstance().listenerImpl.onTaskMovedToFront(RunningTaskInfo())
// Make sure TaskStackChangeListeners' Handler posts the callback before checking state.
- getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isFalse() }
+ runOnMainSync { assertThat(overlay.isOpen).isFalse() }
}
@Test
fun testTaskStackChanged_allAppsClosed_overlayStaysOpen() {
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
- getInstrumentation().runOnMainSync {
- taskbarContext.controllers.sharedState?.allAppsVisible = false
- }
+ runOnMainSync { taskbarContext.controllers.sharedState?.allAppsVisible = false }
TaskStackChangeListeners.getInstance().listenerImpl.onTaskStackChanged()
- getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isTrue() }
+ runOnMainSync { assertThat(overlay.isOpen).isTrue() }
}
@Test
fun testTaskStackChanged_allAppsOpen_closesOverlay() {
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
- getInstrumentation().runOnMainSync {
- taskbarContext.controllers.sharedState?.allAppsVisible = true
- }
+ runOnMainSync { taskbarContext.controllers.sharedState?.allAppsVisible = true }
TaskStackChangeListeners.getInstance().listenerImpl.onTaskStackChanged()
- getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isFalse() }
+ runOnMainSync { assertThat(overlay.isOpen).isFalse() }
}
@Test
@@ -198,7 +191,7 @@
TestOverlayView.show(context).apply { type = TYPE_OPTIONS_POPUP }
}
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
overlayController.updateLauncherDeviceProfile(
overlayController.launcherDeviceProfile
.toBuilder(context)
@@ -217,7 +210,7 @@
TestOverlayView.show(context).apply { type = TYPE_TASKBAR_ALL_APPS }
}
- getInstrumentation().runOnMainSync {
+ runOnMainSync {
overlayController.updateLauncherDeviceProfile(
overlayController.launcherDeviceProfile
.toBuilder(context)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/MultiStateCallbackTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/MultiStateCallbackTest.java
new file mode 100644
index 0000000..0ff142a
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/MultiStateCallbackTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.launcher3.util.LauncherMultivalentJUnit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+@SmallTest
+@RunWith(LauncherMultivalentJUnit.class)
+public class MultiStateCallbackTest {
+
+ private int mFlagCount = 0;
+ private int getNextStateFlag() {
+ int index = 1 << mFlagCount;
+ mFlagCount++;
+ return index;
+ }
+
+ private final MultiStateCallback mMultiStateCallback = new MultiStateCallback(new String[0]);
+ private final Runnable mCallback = spy(new Runnable() {
+ @Override
+ public void run() {}
+ });
+ private final Consumer<Boolean> mListener = spy(new Consumer<Boolean>() {
+ @Override
+ public void accept(Boolean isOn) {}
+ });
+
+ @Test
+ public void testSetState_trackedProperly() {
+ int watchedAnime = getNextStateFlag();
+
+ assertThat(mMultiStateCallback.getState()).isEqualTo(0);
+ assertThat(mMultiStateCallback.hasStates(watchedAnime)).isFalse();
+
+ mMultiStateCallback.setState(watchedAnime);
+
+ assertThat(mMultiStateCallback.getState()).isEqualTo(watchedAnime);
+ assertThat(mMultiStateCallback.hasStates(watchedAnime)).isTrue();
+ }
+
+ @Test
+ public void testSetState_withMultipleStates_trackedProperly() {
+ int watchedAnime = getNextStateFlag();
+ int sharedMemes = getNextStateFlag();
+
+ mMultiStateCallback.setState(watchedAnime);
+ mMultiStateCallback.setState(sharedMemes);
+
+ assertThat(mMultiStateCallback.getState()).isEqualTo(watchedAnime | sharedMemes);
+ assertThat(mMultiStateCallback.hasStates(watchedAnime)).isTrue();
+ assertThat(mMultiStateCallback.hasStates(sharedMemes)).isTrue();
+ assertThat(mMultiStateCallback.hasStates(watchedAnime | sharedMemes)).isTrue();
+ }
+
+ @Test
+ public void testClearState_trackedProperly() {
+ int lovedAnime = getNextStateFlag();
+
+ mMultiStateCallback.setState(lovedAnime);
+ mMultiStateCallback.clearState(lovedAnime);
+
+ assertThat(mMultiStateCallback.getState()).isEqualTo(0);
+ assertThat(mMultiStateCallback.hasStates(lovedAnime)).isFalse();
+ }
+
+ @Test
+ public void testClearState_withMultipleState_trackedProperly() {
+ int lovedAnime = getNextStateFlag();
+ int talkedAboutAnime = getNextStateFlag();
+
+ mMultiStateCallback.setState(lovedAnime);
+ mMultiStateCallback.setState(talkedAboutAnime);
+ mMultiStateCallback.clearState(talkedAboutAnime);
+
+ assertThat(mMultiStateCallback.getState()).isEqualTo(lovedAnime);
+ assertThat(mMultiStateCallback.hasStates(lovedAnime)).isTrue();
+ assertThat(mMultiStateCallback.hasStates(talkedAboutAnime)).isFalse();
+ assertThat(mMultiStateCallback.hasStates(lovedAnime | talkedAboutAnime)).isFalse();
+ }
+
+ @Test
+ public void testCallbackDoesNotRun_withoutState() {
+ int watchedOnePiece = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedOnePiece, mCallback);
+
+ verify(mCallback, never()).run();
+ }
+
+ @Test
+ public void testCallbackDoesNotRun_whenNotTracked() {
+ int watchedJujutsuKaisen = getNextStateFlag();
+
+ mMultiStateCallback.setState(watchedJujutsuKaisen);
+
+ verify(mCallback, never()).run();
+ }
+
+ @Test
+ public void testCallbackRuns_afterTrackedAndStateSet() {
+ int watchedHunterXHunter = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedHunterXHunter, mCallback);
+ mMultiStateCallback.setState(watchedHunterXHunter);
+
+ verify(mCallback, times(1)).run();
+ }
+
+ @Test
+ public void testCallbackRuns_onUiThread() {
+ int watchedHunterXHunter = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedHunterXHunter, mCallback);
+ mMultiStateCallback.setStateOnUiThread(watchedHunterXHunter);
+
+ runOnMainSync(() -> verify(mCallback, times(1)).run());
+ }
+
+ @Test
+ public void testCallbackRuns_agnosticallyToCallOrder() {
+ int watchedFullMetalAlchemist = getNextStateFlag();
+
+ mMultiStateCallback.setState(watchedFullMetalAlchemist);
+ mMultiStateCallback.runOnceAtState(watchedFullMetalAlchemist, mCallback);
+
+ verify(mCallback, times(1)).run();
+ }
+
+ @Test
+ public void testCallbackRuns_onlyOnceAfterStateSet() {
+ int watchedBleach = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedBleach, mCallback);
+ mMultiStateCallback.setState(watchedBleach);
+ mMultiStateCallback.setState(watchedBleach);
+
+ verify(mCallback, times(1)).run();
+ }
+
+ @Test
+ public void testCallbackRuns_onlyOnceAfterClearState() {
+ int rememberedGreatShow = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(rememberedGreatShow, mCallback);
+ mMultiStateCallback.setState(rememberedGreatShow);
+ mMultiStateCallback.clearState(rememberedGreatShow);
+ mMultiStateCallback.setState(rememberedGreatShow);
+
+ verify(mCallback, times(1)).run();
+ }
+
+ @Test
+ public void testCallbackDoesNotRun_withoutFullStateSet() {
+ int watchedMobPsycho = getNextStateFlag();
+ int watchedVinlandSaga = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedMobPsycho | watchedVinlandSaga, mCallback);
+ mMultiStateCallback.setState(watchedMobPsycho);
+
+ verify(mCallback, times(0)).run();
+ }
+
+ @Test
+ public void testCallbackRuns_withFullStateSet_agnosticallyToCallOrder() {
+ int watchedReZero = getNextStateFlag();
+ int watchedJojosBizareAdventure = getNextStateFlag();
+
+ mMultiStateCallback.setState(watchedJojosBizareAdventure);
+ mMultiStateCallback.runOnceAtState(watchedReZero | watchedJojosBizareAdventure, mCallback);
+ mMultiStateCallback.setState(watchedReZero);
+
+ verify(mCallback, times(1)).run();
+ }
+
+ @Test
+ public void testCallbackRuns_withFullStateSet_asIntegerMask() {
+ int watchedPokemon = getNextStateFlag();
+ int watchedDigimon = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedPokemon | watchedDigimon, mCallback);
+ mMultiStateCallback.setState(watchedPokemon | watchedDigimon);
+
+ verify(mCallback, times(1)).run();
+ }
+
+ @Test
+ public void testCallbackDoesNotRun_afterClearState() {
+ int watchedMonster = getNextStateFlag();
+ int watchedPingPong = getNextStateFlag();
+
+ mMultiStateCallback.runOnceAtState(watchedMonster | watchedPingPong, mCallback);
+ mMultiStateCallback.setState(watchedMonster);
+ mMultiStateCallback.clearState(watchedMonster);
+ mMultiStateCallback.setState(watchedPingPong);
+
+ verify(mCallback, times(0)).run();
+ }
+
+ @Test
+ public void testlistenerRuns_multipleTimes() {
+ int watchedSteinsGate = getNextStateFlag();
+
+ mMultiStateCallback.addChangeListener(watchedSteinsGate, mListener);
+ mMultiStateCallback.setState(watchedSteinsGate);
+
+ // Called exactly one
+ verify(mListener, times(1)).accept(anyBoolean());
+ // Called exactly once with isOn = true
+ verify(mListener, times(1)).accept(eq(true));
+ // Never called with isOn = false
+ verify(mListener, times(0)).accept(eq(false));
+
+ mMultiStateCallback.clearState(watchedSteinsGate);
+
+ // Called exactly twice
+ verify(mListener, times(2)).accept(anyBoolean());
+ // Called exactly once with isOn = true
+ verify(mListener, times(1)).accept(eq(true));
+ // Called exactly once with isOn = false
+ verify(mListener, times(1)).accept(eq(false));
+ }
+
+ @Test
+ public void testlistenerDoesNotRun_forUnchangedState() {
+ int watchedSteinsGate = getNextStateFlag();
+
+ mMultiStateCallback.addChangeListener(watchedSteinsGate, mListener);
+ mMultiStateCallback.setState(watchedSteinsGate);
+ mMultiStateCallback.setState(watchedSteinsGate);
+
+ // State remained unchanged
+ verify(mListener, times(1)).accept(anyBoolean());
+ // Called exactly once with isOn = true
+ verify(mListener, times(1)).accept(eq(true));
+ }
+
+ private static void runOnMainSync(Runnable runnable) {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable);
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
new file mode 100644
index 0000000..80b9489
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
+import com.android.quickstep.DeviceConfigWrapper;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.NavHandle;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.TopTaskTracker;
+import com.android.quickstep.util.TestExtensions;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NavHandleLongPressInputConsumerTest {
+
+ private static final float TOUCH_SLOP = 10;
+ private static final float SQUARED_TOUCH_SLOP = 100;
+
+ private final AtomicBoolean mLongPressTriggered = new AtomicBoolean();
+ private final Runnable mLongPressRunnable = () -> mLongPressTriggered.set(true);
+ private NavHandleLongPressInputConsumer mUnderTest;
+ private SandboxContext mContext;
+ private float mScreenWidth;
+ @Mock InputConsumer mDelegate;
+ @Mock InputMonitorCompat mInputMonitor;
+ @Mock RecentsAnimationDeviceState mDeviceState;
+ @Mock NavHandle mNavHandle;
+ @Mock GestureState mGestureState;
+ @Mock NavHandleLongPressHandler mNavHandleLongPressHandler;
+ @Mock TopTaskTracker mTopTaskTracker;
+ @Mock TopTaskTracker.CachedTaskInfo mTaskInfo;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mTopTaskTracker.getCachedTopTask(anyBoolean())).thenReturn(mTaskInfo);
+ when(mDeviceState.getSquaredTouchSlop()).thenReturn(SQUARED_TOUCH_SLOP);
+ when(mDelegate.allowInterceptByParent()).thenReturn(true);
+ MAIN_EXECUTOR.getHandler().removeCallbacks(mLongPressRunnable);
+ mLongPressTriggered.set(false);
+ when(mNavHandleLongPressHandler.getLongPressRunnable(any())).thenReturn(mLongPressRunnable);
+ initializeObjectUnderTest();
+ }
+
+ @After
+ public void tearDown() {
+ mContext.onDestroy();
+ }
+
+ @Test
+ public void testGetType() {
+ assertThat(mUnderTest.getType() & InputConsumer.TYPE_NAV_HANDLE_LONG_PRESS).isNotEqualTo(0);
+ }
+
+ @Test
+ public void testDelegateDisallowsTouchIntercept() {
+ when(mDelegate.allowInterceptByParent()).thenReturn(false);
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+
+ verify(mDelegate).onMotionEvent(any());
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ verify(mNavHandleLongPressHandler, never()).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testDelegateDisallowsTouchInterceptAfterTouchDown() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+
+ // Delegate should still get touches unless long press is triggered.
+ verify(mDelegate).onMotionEvent(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+
+ when(mDelegate.allowInterceptByParent()).thenReturn(false);
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_MOVE));
+
+ // Delegate should still get motion events unless long press is triggered.
+ verify(mDelegate, times(2)).onMotionEvent(any());
+ // But our handler should be cancelled.
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressTriggered() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
+ assertTrue(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressTriggeredWithSlightVerticalMovement() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
+ -(TOUCH_SLOP - 1)));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
+ assertTrue(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressTriggeredWithSlightHorizontalMovement() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
+ mScreenWidth / 2f - (TOUCH_SLOP - 1), 0));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
+ assertTrue(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressTriggeredWithExtendedTwoStageDuration() {
+ try (AutoCloseable flag = overrideTwoStageFlag(true)) {
+ // Reinitialize to pick up updated flag state.
+ initializeObjectUnderTest();
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
+ mScreenWidth / 2f - (TOUCH_SLOP - 1), 0));
+ // We have entered the second stage, so the normal timeout shouldn't trigger.
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+
+ // After an extended time, the long press should trigger.
+ float extendedDurationMultiplier =
+ (DeviceConfigWrapper.get().getTwoStageDurationPercentage() / 100f);
+ SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout()
+ * (extendedDurationMultiplier - 1))); // -1 because we already waited 1x
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
+ assertTrue(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testLongPressTriggeredWithNormalDurationInFirstStage() {
+ try (AutoCloseable flag = overrideTwoStageFlag(true)) {
+ // Reinitialize to pick up updated flag state.
+ initializeObjectUnderTest();
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ // We have not entered the second stage, so the normal timeout should trigger.
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
+ assertTrue(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testLongPressAbortedByTouchUp() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() - 10);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_UP));
+ // Wait past the long press timeout, to be extra sure it wouldn't have triggered.
+ SystemClock.sleep(20);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressAbortedByTouchCancel() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() - 10);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_CANCEL));
+ // Wait past the long press timeout, to be extra sure it wouldn't have triggered.
+ SystemClock.sleep(20);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressAbortedByTouchSlopPassedVertically() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() - 10);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
+ -(TOUCH_SLOP + 1)));
+ // Wait past the long press timeout, to be extra sure it wouldn't have triggered.
+ SystemClock.sleep(20);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressAbortedByTouchSlopPassedHorizontally() {
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() - 10);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
+ mScreenWidth / 2f - (TOUCH_SLOP + 1), 0));
+ // Wait past the long press timeout, to be extra sure it wouldn't have triggered.
+ SystemClock.sleep(20);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testLongPressAbortedByTouchSlopPassedVertically_twoStageEnabled() {
+ try (AutoCloseable flag = overrideTwoStageFlag(true)) {
+ // Reinitialize to pick up updated flag state.
+ initializeObjectUnderTest();
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ // Enter the second stage.
+ mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
+ -(TOUCH_SLOP - 1)));
+ // Normal duration shouldn't trigger.
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+
+ // Move out of the second stage.
+ mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
+ -(TOUCH_SLOP + 1)));
+ // Wait past the extended long press timeout, to be sure it wouldn't have triggered.
+ float extendedDurationMultiplier =
+ (DeviceConfigWrapper.get().getTwoStageDurationPercentage() / 100f);
+ SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout()
+ * (extendedDurationMultiplier - 1))); // -1 because we already waited 1x
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ // Touch cancelled.
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testLongPressAbortedByTouchSlopPassedHorizontally_twoStageEnabled() {
+ try (AutoCloseable flag = overrideTwoStageFlag(true)) {
+ // Reinitialize to pick up updated flag state.
+ initializeObjectUnderTest();
+
+ mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
+ // Enter the second stage.
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
+ mScreenWidth / 2f - (TOUCH_SLOP - 1), 0));
+ // Normal duration shouldn't trigger.
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+
+ // Move out of the second stage.
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
+ mScreenWidth / 2f - (TOUCH_SLOP + 1), 0));
+ // Wait past the extended long press timeout, to be sure it wouldn't have triggered.
+ float extendedDurationMultiplier =
+ (DeviceConfigWrapper.get().getTwoStageDurationPercentage() / 100f);
+ SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout()
+ * (extendedDurationMultiplier - 1))); // -1 because we already waited 1x
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, times(1)).onTouchStarted(any());
+ // Touch cancelled.
+ verify(mNavHandleLongPressHandler, times(1)).onTouchFinished(any(), any());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testTouchOutsideNavHandleIgnored() {
+ // Touch the far left side of the screen. (y=0 is top of navbar region, picked arbitrarily)
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_DOWN, 0, 0));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ // Should be ignored because the x position was not centered in the navbar region.
+ assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
+ assertFalse(mLongPressTriggered.get());
+ verify(mNavHandleLongPressHandler, never()).onTouchStarted(any());
+ verify(mNavHandleLongPressHandler, never()).onTouchFinished(any(), any());
+ }
+
+ @Test
+ public void testHoverPassedToDelegate() {
+ // Regardless of whether the delegate wants us to intercept, we tell it about hover events.
+ when(mDelegate.allowInterceptByParent()).thenReturn(false);
+ mUnderTest.onHoverEvent(generateCenteredMotionEvent(ACTION_HOVER_ENTER));
+
+ verify(mDelegate).onHoverEvent(any());
+
+ when(mDelegate.allowInterceptByParent()).thenReturn(true);
+ mUnderTest.onHoverEvent(generateCenteredMotionEvent(ACTION_HOVER_ENTER));
+
+ verify(mDelegate, times(2)).onHoverEvent(any());
+ }
+
+ private void initializeObjectUnderTest() {
+ if (mContext != null) {
+ mContext.onDestroy();
+ }
+ mContext = new SandboxContext(getApplicationContext());
+ mContext.putObject(TopTaskTracker.INSTANCE, mTopTaskTracker);
+ mScreenWidth = DisplayController.INSTANCE.get(mContext).getInfo().currentSize.x;
+ mUnderTest = new NavHandleLongPressInputConsumer(mContext, mDelegate, mInputMonitor,
+ mDeviceState, mNavHandle, mGestureState);
+ mUnderTest.setNavHandleLongPressHandler(mNavHandleLongPressHandler);
+ }
+
+ /** Generate a motion event centered horizontally in the screen. */
+ private MotionEvent generateCenteredMotionEvent(int motionAction) {
+ return generateCenteredMotionEventWithYOffset(motionAction, 0);
+ }
+
+ /** Generate a motion event centered horizontally in the screen, with y offset. */
+ private MotionEvent generateCenteredMotionEventWithYOffset(int motionAction, float y) {
+ return generateMotionEvent(motionAction, mScreenWidth / 2f, y);
+ }
+
+ private static MotionEvent generateMotionEvent(int motionAction, float x, float y) {
+ return MotionEvent.obtain(0, 0, motionAction, x, y, 0);
+ }
+
+ private static AutoCloseable overrideTwoStageFlag(boolean value) {
+ return TestExtensions.overrideNavConfigFlag(
+ "ENABLE_LPNH_TWO_STAGES",
+ value,
+ () -> DeviceConfigWrapper.get().getEnableLpnhTwoStages());
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
index 070eeaf..d2479bc 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/logging/SettingsChangeLoggerTest.kt
@@ -20,9 +20,23 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.LauncherPrefs
-import com.android.launcher3.LauncherPrefs.Companion.backedUpItem
+import com.android.launcher3.LauncherPrefs.Companion.ALLOW_ROTATION
+import com.android.launcher3.LauncherPrefs.Companion.THEMED_ICONS
+import com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY
import com.android.launcher3.logging.InstanceId
import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_SUGGESTIONS_ENABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_ROTATION_DISABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_ROTATION_ENABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_DISABLED
+import com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY
+import com.google.android.apps.nexuslauncher.PrefKey.KEY_ENABLE_MINUS_ONE
+import com.google.android.apps.nexuslauncher.PrefKey.OVERVIEW_SUGGESTED_ACTIONS
+import com.google.android.apps.nexuslauncher.PrefKey.SMARTSPACE_ON_HOME_SCREEN
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -52,30 +66,41 @@
@Captor private lateinit var mEventCaptor: ArgumentCaptor<StatsLogManager.EventEnum>
+ private var mDefaultThemedIcons = false
+ private var mDefaultAllowRotation = false
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mStatsLogManager.logger()).doReturn(mMockLogger)
whenever(mStatsLogManager.logger().withInstanceId(any())).doReturn(mMockLogger)
+ mDefaultThemedIcons = LauncherPrefs.get(mContext).get(THEMED_ICONS)
+ mDefaultAllowRotation = LauncherPrefs.get(mContext).get(ALLOW_ROTATION)
+ // To match the default value of THEMED_ICONS
+ LauncherPrefs.get(mContext).put(THEMED_ICONS, false)
+ // To match the default value of ALLOW_ROTATION
+ LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = false)
mSystemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager)
}
@After
fun tearDown() {
- mSystemUnderTest.close()
+ LauncherPrefs.get(mContext).put(THEMED_ICONS, mDefaultThemedIcons)
+ LauncherPrefs.get(mContext).put(ALLOW_ROTATION, mDefaultAllowRotation)
}
@Test
fun loggingPrefs_correctDefaultValue() {
- assertThat(mSystemUnderTest.loggingPrefs["pref_allowRotation"]!!.defaultValue).isFalse()
- assertThat(mSystemUnderTest.loggingPrefs["pref_add_icon_to_home"]!!.defaultValue).isTrue()
- assertThat(mSystemUnderTest.loggingPrefs["pref_overview_action_suggestions"]!!.defaultValue)
- .isTrue()
- assertThat(mSystemUnderTest.loggingPrefs["pref_smartspace_home_screen"]!!.defaultValue)
- .isTrue()
- assertThat(mSystemUnderTest.loggingPrefs["pref_enable_minus_one"]!!.defaultValue).isTrue()
+ val systemUnderTest = SettingsChangeLogger(mContext, mStatsLogManager)
+
+ assertThat(systemUnderTest.loggingPrefs[ALLOW_ROTATION_PREFERENCE_KEY]!!.defaultValue)
+ .isFalse()
+ assertThat(systemUnderTest.loggingPrefs[ADD_ICON_PREFERENCE_KEY]!!.defaultValue).isTrue()
+ assertThat(systemUnderTest.loggingPrefs[OVERVIEW_SUGGESTED_ACTIONS]!!.defaultValue).isTrue()
+ assertThat(systemUnderTest.loggingPrefs[SMARTSPACE_ON_HOME_SCREEN]!!.defaultValue).isTrue()
+ assertThat(systemUnderTest.loggingPrefs[KEY_ENABLE_MINUS_ONE]!!.defaultValue).isTrue()
}
@Test
@@ -86,46 +111,37 @@
val capturedEvents = mEventCaptor.allValues
assertThat(capturedEvents.isNotEmpty()).isTrue()
verifyDefaultEvent(capturedEvents)
- // pref_allowRotation false
- assertThat(capturedEvents.any { it.id == 616 }).isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_HOME_SCREEN_ROTATION_DISABLED.id })
+ .isTrue()
}
@Test
- fun logSnapshot_updateValue() {
- LauncherPrefs.get(mContext)
- .put(
- item =
- backedUpItem(
- sharedPrefKey = "pref_allowRotation",
- defaultValue = false,
- ),
- value = true
- )
+ fun logSnapshot_updateAllowRotation() {
+ LauncherPrefs.get(mContext).put(item = ALLOW_ROTATION, value = true)
- mSystemUnderTest.logSnapshot(mInstanceId)
+ // This a new object so the values of mLoggablePrefs will be different
+ SettingsChangeLogger(mContext, mStatsLogManager).logSnapshot(mInstanceId)
verify(mMockLogger, atLeastOnce()).log(mEventCaptor.capture())
val capturedEvents = mEventCaptor.allValues
assertThat(capturedEvents.isNotEmpty()).isTrue()
verifyDefaultEvent(capturedEvents)
- // pref_allowRotation true
- assertThat(capturedEvents.any { it.id == 615 }).isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_HOME_SCREEN_ROTATION_ENABLED.id })
+ .isTrue()
}
private fun verifyDefaultEvent(capturedEvents: MutableList<StatsLogManager.EventEnum>) {
- // LAUNCHER_NOTIFICATION_DOT_ENABLED
- assertThat(capturedEvents.any { it.id == 611 }).isTrue()
- // LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON
- assertThat(capturedEvents.any { it.id == 625 }).isTrue()
- // LAUNCHER_THEMED_ICON_DISABLED
- assertThat(capturedEvents.any { it.id == 837 }).isTrue()
- // pref_add_icon_to_home true
- assertThat(capturedEvents.any { it.id == 613 }).isTrue()
- // pref_overview_action_suggestions true
- assertThat(capturedEvents.any { it.id == 619 }).isTrue()
- // pref_smartspace_home_screen true
- assertThat(capturedEvents.any { it.id == 621 }).isTrue()
- // pref_enable_minus_one true
+ assertThat(capturedEvents.any { it.id == LAUNCHER_NOTIFICATION_DOT_ENABLED.id }).isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON.id })
+ .isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_THEMED_ICON_DISABLED.id }).isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED.id })
+ .isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_ALL_APPS_SUGGESTIONS_ENABLED.id })
+ .isTrue()
+ assertThat(capturedEvents.any { it.id == LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED.id })
+ .isTrue()
+ // LAUNCHER_GOOGLE_APP_SWIPE_LEFT_ENABLED
assertThat(capturedEvents.any { it.id == 617 }).isTrue()
}
}
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
new file mode 100644
index 0000000..cdfbd16
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsDeviceProfileRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.recents.data
+
+class FakeRecentsDeviceProfileRepository : RecentsDeviceProfileRepository {
+ private var recentsDeviceProfile =
+ RecentsDeviceProfile(
+ isLargeScreen = false,
+ widthPx = 1080,
+ heightPx = 1920,
+ )
+
+ override fun getRecentsDeviceProfile() = recentsDeviceProfile
+
+ fun setRecentsDeviceProfile(newValue: RecentsDeviceProfile) {
+ recentsDeviceProfile = newValue
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsRotationStateRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsRotationStateRepository.kt
new file mode 100644
index 0000000..c328672
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentsRotationStateRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.view.Surface
+
+class FakeRecentsRotationStateRepository : RecentsRotationStateRepository {
+ private var recentsRotationState =
+ RecentsRotationState(
+ activityRotation = Surface.ROTATION_0,
+ orientationHandlerRotation = Surface.ROTATION_0
+ )
+
+ override fun getRecentsRotationState() = recentsRotationState
+
+ fun setRecentsRotationState(newValue: RecentsRotationState) {
+ recentsRotationState = newValue
+ }
+}
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 242bc73..fee4979 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
@@ -23,10 +23,12 @@
import com.android.systemui.shared.recents.model.Task
import com.google.common.truth.Truth.assertThat
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
class FakeTaskIconDataSource : TaskIconDataSource {
- val taskIdToDrawable: Map<Int, Drawable> = (0..10).associateWith { mock() }
+ val taskIdToDrawable: Map<Int, Drawable> = (0..10).associateWith { mockCopyableDrawable() }
+
val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
var shouldLoadSynchronously: Boolean = true
@@ -49,6 +51,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) }
+ }
}
fun Task.assertHasIconDataFromSource(fakeTaskIconDataSource: FakeTaskIconDataSource) {
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 19990a8..d94a351 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
@@ -20,24 +20,58 @@
import com.android.systemui.shared.recents.model.ThumbnailData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
class FakeTasksRepository : RecentTasksRepository {
private var thumbnailDataMap: Map<Int, ThumbnailData> = emptyMap()
+ 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
override fun getTaskDataById(taskId: Int): Flow<Task?> =
- getAllTaskData().map { taskList -> taskList.firstOrNull { it.key.id == taskId } }
+ combine(getAllTaskData(), visibleTasks) { taskList, visibleTasks ->
+ taskList.filter { visibleTasks.contains(it.key.id) }
+ }
+ .map { taskList ->
+ val task = taskList.firstOrNull { it.key.id == taskId } ?: return@map null
+ Task(task).apply {
+ thumbnail = thumbnailOverrideMap[taskId] ?: task.thumbnail
+ icon = task.icon
+ titleDescription = task.titleDescription
+ title = task.title
+ }
+ }
override fun getThumbnailById(taskId: Int): Flow<ThumbnailData?> =
getTaskDataById(taskId).map { it?.thumbnail }
override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
visibleTasks.value = visibleTaskIdList
- tasks.value = tasks.value.map { it.apply { thumbnail = thumbnailDataMap[it.key.id] } }
+ tasks.value =
+ tasks.value.map {
+ it.apply {
+ thumbnail = thumbnailDataMap[it.key.id]
+ taskIconDataMap[it.key.id].let { taskIconData ->
+ icon = taskIconData?.icon
+ titleDescription = taskIconData?.contentDescription
+ title = taskIconData?.title
+ }
+ }
+ }
+ 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>) {
@@ -47,4 +81,8 @@
fun seedThumbnailData(thumbnailDataMap: Map<Int, ThumbnailData>) {
this.thumbnailDataMap = thumbnailDataMap
}
+
+ fun seedIconData(iconDataMap: Map<Int, TaskIconQueryResponse>) {
+ this.taskIconDataMap = iconDataMap
+ }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
similarity index 84%
rename from quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
index eff926d..e74fe4b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsDeviceProfileRepositoryImplTest.kt
@@ -25,12 +25,12 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-/** Test for [RecentsDeviceProfileRepository] */
+/** Test for [RecentsDeviceProfileRepositoryImpl] */
@RunWith(AndroidJUnit4::class)
-class RecentsDeviceProfileRepositoryTest : FakeInvariantDeviceProfileTest() {
+class RecentsDeviceProfileRepositoryImplTest : FakeInvariantDeviceProfileTest() {
private val recentsViewContainer = mock<RecentsViewContainer>()
- private val systemUnderTest = RecentsDeviceProfileRepository(recentsViewContainer)
+ private val systemUnderTest = RecentsDeviceProfileRepositoryImpl(recentsViewContainer)
@Test
fun deviceProfileMappedCorrectly() {
@@ -39,6 +39,6 @@
whenever(recentsViewContainer.deviceProfile).thenReturn(tabletDeviceProfile)
assertThat(systemUnderTest.getRecentsDeviceProfile())
- .isEqualTo(RecentsDeviceProfileRepository.RecentsDeviceProfile(isLargeScreen = true))
+ .isEqualTo(RecentsDeviceProfile(isLargeScreen = true, widthPx = 1600, heightPx = 2560))
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryImplTest.kt
similarity index 71%
rename from quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryImplTest.kt
index 1f4da26..017f037 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/RecentsRotationStateRepositoryImplTest.kt
@@ -16,26 +16,32 @@
package com.android.quickstep.recents.data
+import android.view.Surface.ROTATION_270
import android.view.Surface.ROTATION_90
+import com.android.quickstep.orientation.SeascapePagedViewHandler
import com.android.quickstep.util.RecentsOrientedState
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-/** Test for [RecentsRotationStateRepository] */
-class RecentsRotationStateRepositoryTest {
+/** Test for [RecentsRotationStateRepositoryImpl] */
+class RecentsRotationStateRepositoryImplTest {
private val recentsOrientedState = mock<RecentsOrientedState>()
- private val systemUnderTest = RecentsRotationStateRepository(recentsOrientedState)
+ private val systemUnderTest = RecentsRotationStateRepositoryImpl(recentsOrientedState)
@Test
fun orientedStateMappedCorrectly() {
whenever(recentsOrientedState.recentsActivityRotation).thenReturn(ROTATION_90)
+ whenever(recentsOrientedState.orientationHandler).thenReturn(SeascapePagedViewHandler())
assertThat(systemUnderTest.getRecentsRotationState())
.isEqualTo(
- RecentsRotationStateRepository.RecentsRotationState(activityRotation = ROTATION_90)
+ RecentsRotationState(
+ activityRotation = ROTATION_90,
+ orientationHandlerRotation = ROTATION_270
+ )
)
}
}
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 88fa190..b34e156 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
@@ -18,9 +18,13 @@
import android.content.ComponentName
import android.content.Intent
+import android.graphics.Bitmap
+import android.view.Surface
+import com.android.quickstep.task.thumbnail.TaskThumbnailViewModelTest
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.drop
@@ -30,6 +34,8 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
class TasksRepositoryTest {
@@ -186,6 +192,90 @@
assertThat(taskFlowValuesList[1]!!.thumbnail!!.thumbnail).isEqualTo(bitmap2)
}
+ @Test
+ fun addThumbnailOverrideOverrideThumbnails() = 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))
+
+ // .drop(1) to ignore initial null content before from thumbnail was loaded.
+ assertThat(systemUnderTest.getThumbnailById(1).drop(1).first()!!.thumbnail)
+ .isEqualTo(bitmap1)
+ assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail)
+ .isEqualTo(thumbnailOverride2.thumbnail)
+ }
+
+ @Test
+ fun addThumbnailOverrideMultipleOverrides() = 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() = 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))
+
+ // .drop(1) to ignore initial null content before from thumbnail was loaded.
+ assertThat(systemUnderTest.getThumbnailById(1).first()!!.thumbnail)
+ .isEqualTo(thumbnailOverride1.thumbnail)
+ assertThat(systemUnderTest.getThumbnailById(2).drop(1).first()!!.thumbnail)
+ .isEqualTo(bitmap2)
+ }
+
+ @Test
+ fun addThumbnailOverrideDoesNotOverrideInvisibleTasks() = 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))
+
+ // .drop(1) to ignore initial null content before from thumbnail was loaded.
+ assertThat(systemUnderTest.getThumbnailById(1).drop(1).first()!!.thumbnail)
+ .isEqualTo(bitmap1)
+ assertThat(systemUnderTest.getThumbnailById(2).first()!!.thumbnail).isEqualTo(bitmap2)
+ }
+
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000))
+
+ private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): 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)
+ }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt
index e657d59..02f1d11 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/usecase/GetThumbnailPositionUseCaseTest.kt
@@ -24,9 +24,9 @@
import android.graphics.Rect
import android.view.Surface.ROTATION_90
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.quickstep.recents.data.FakeRecentsDeviceProfileRepository
+import com.android.quickstep.recents.data.FakeRecentsRotationStateRepository
import com.android.quickstep.recents.data.FakeTasksRepository
-import com.android.quickstep.recents.data.RecentsDeviceProfileRepository
-import com.android.quickstep.recents.data.RecentsRotationStateRepository
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MissingThumbnail
import com.android.systemui.shared.recents.model.Task
@@ -56,8 +56,8 @@
}
)
- private val deviceProfileRepository = mock<RecentsDeviceProfileRepository>()
- private val rotationStateRepository = mock<RecentsRotationStateRepository>()
+ private val deviceProfileRepository = FakeRecentsDeviceProfileRepository()
+ private val rotationStateRepository = FakeRecentsRotationStateRepository()
private val tasksRepository = FakeTasksRepository()
private val previewPositionHelper = mock<PreviewPositionHelper>()
@@ -93,15 +93,18 @@
tasksRepository.setVisibleTasks(listOf(TASK_ID))
val isLargeScreen = true
+ deviceProfileRepository.setRecentsDeviceProfile(
+ deviceProfileRepository.getRecentsDeviceProfile().copy(isLargeScreen = isLargeScreen)
+ )
val activityRotation = ROTATION_90
+ rotationStateRepository.setRecentsRotationState(
+ rotationStateRepository
+ .getRecentsRotationState()
+ .copy(activityRotation = activityRotation)
+ )
val isRtl = true
val isRotated = true
- whenever(deviceProfileRepository.getRecentsDeviceProfile())
- .thenReturn(RecentsDeviceProfileRepository.RecentsDeviceProfile(isLargeScreen))
- whenever(rotationStateRepository.getRecentsRotationState())
- .thenReturn(RecentsRotationStateRepository.RecentsRotationState(activityRotation))
-
whenever(previewPositionHelper.matrix).thenReturn(MATRIX)
whenever(previewPositionHelper.isOrientationChanged).thenReturn(isRotated)
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
new file mode 100644
index 0000000..b3caf2d
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/viewmodel/RecentsViewModelTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.viewmodel
+
+import android.content.ComponentName
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.view.Surface
+import com.android.quickstep.recents.data.FakeTasksRepository
+import com.android.quickstep.task.thumbnail.TaskThumbnailViewModelTest
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+class RecentsViewModelTest {
+ private val tasksRepository = FakeTasksRepository()
+ private val recentsViewData = RecentsViewData()
+ private val systemUnderTest = RecentsViewModel(tasksRepository, recentsViewData)
+
+ private val tasks = (0..5).map(::createTaskWithId)
+
+ @Test
+ fun taskVisibilityControlThumbnailsAvailability() = runTest {
+ val thumbnailData1 = createThumbnailData()
+ val thumbnailData2 = createThumbnailData()
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.seedThumbnailData(mapOf(1 to thumbnailData1, 2 to thumbnailData2))
+
+ val thumbnailDataFlow1 = tasksRepository.getThumbnailById(1)
+ val thumbnailDataFlow2 = tasksRepository.getThumbnailById(2)
+
+ systemUnderTest.refreshAllTaskData()
+
+ assertThat(thumbnailDataFlow1.first()).isNull()
+ assertThat(thumbnailDataFlow2.first()).isNull()
+
+ systemUnderTest.updateVisibleTasks(listOf(1, 2))
+
+ assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
+ assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
+
+ systemUnderTest.updateVisibleTasks(listOf(1))
+
+ assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
+ assertThat(thumbnailDataFlow2.first()).isNull()
+
+ systemUnderTest.onReset()
+
+ assertThat(thumbnailDataFlow1.first()).isNull()
+ assertThat(thumbnailDataFlow2.first()).isNull()
+ }
+
+ @Test
+ fun thumbnailOverrideWaitAndReset() = runTest {
+ val thumbnailData1 = createThumbnailData()
+ val thumbnailData2 = createThumbnailData()
+ val thumbnailDataOverride = createThumbnailData()
+ 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)
+
+ val thumbnailUpdate = mapOf(2 to thumbnailDataOverride)
+ systemUnderTest.setRunningTaskShowScreenshot(true)
+ systemUnderTest.addOrUpdateThumbnailOverride(thumbnailUpdate)
+
+ systemUnderTest.waitForRunningTaskShowScreenshotToUpdate()
+ systemUnderTest.waitForThumbnailsToUpdate(thumbnailUpdate)
+
+ assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
+ assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailDataOverride)
+
+ 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)
+ }
+
+ private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): 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)
+ }
+}
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
new file mode 100644
index 0000000..13e8b09
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/GetSplashSizeUseCaseTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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/SplashAlphaUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCaseTest.kt
new file mode 100644
index 0000000..a584d71
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/SplashAlphaUseCaseTest.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.content.ComponentName
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.view.Surface
+import android.view.Surface.ROTATION_90
+import com.android.quickstep.recents.data.FakeRecentsRotationStateRepository
+import com.android.quickstep.recents.data.FakeTasksRepository
+import com.android.quickstep.recents.data.TaskIconQueryResponse
+import com.android.quickstep.recents.viewmodel.RecentsViewData
+import com.android.quickstep.task.viewmodel.TaskContainerData
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+class SplashAlphaUseCaseTest {
+ private val recentsViewData = RecentsViewData()
+ private val taskContainerData = TaskContainerData()
+ private val taskThumbnailViewData = TaskThumbnailViewData()
+ private val recentTasksRepository = FakeTasksRepository()
+ private val recentsRotationStateRepository = FakeRecentsRotationStateRepository()
+ private val systemUnderTest =
+ SplashAlphaUseCase(
+ recentsViewData,
+ taskContainerData,
+ taskThumbnailViewData,
+ recentTasksRepository,
+ recentsRotationStateRepository
+ )
+
+ @Test
+ fun execute_withNullThumbnail_showsSplash() = runTest {
+ assertThat(systemUnderTest.execute(0).first()).isEqualTo(SPLASH_HIDDEN)
+ }
+
+ @Test
+ fun execute_withTaskSpecificSplashAlpha_showsSplash() = runTest {
+ setupTask(2)
+ taskContainerData.thumbnailSplashProgress.value = 0.7f
+
+ assertThat(systemUnderTest.execute(2).first()).isEqualTo(0.7f)
+ }
+
+ @Test
+ fun execute_withNoGlobalSplashEnabled_doesntShowSplash() = runTest {
+ setupTask(2)
+
+ assertThat(systemUnderTest.execute(2).first()).isEqualTo(SPLASH_HIDDEN)
+ }
+
+ @Test
+ fun execute_withSameAspectRatioAndRotation_withGlobalSplashEnabled_doesntShowSplash() =
+ runTest {
+ setupTask(2)
+ recentsViewData.thumbnailSplashProgress.value = 0.5f
+ taskThumbnailViewData.width.value = THUMBNAIL_WIDTH * 2
+ taskThumbnailViewData.height.value = THUMBNAIL_HEIGHT * 2
+
+ assertThat(systemUnderTest.execute(2).first()).isEqualTo(SPLASH_HIDDEN)
+ }
+
+ @Test
+ fun execute_withDifferentAspectRatioAndSameRotation_showsSplash() = runTest {
+ setupTask(2)
+ recentsViewData.thumbnailSplashProgress.value = 0.5f
+ taskThumbnailViewData.width.value = THUMBNAIL_WIDTH
+ taskThumbnailViewData.height.value = THUMBNAIL_HEIGHT * 2
+
+ assertThat(systemUnderTest.execute(2).first()).isEqualTo(0.5f)
+ }
+
+ @Test
+ fun execute_withSameAspectRatioAndDifferentRotation_showsSplash() = runTest {
+ setupTask(2, createThumbnailData(rotation = ROTATION_90))
+ recentsViewData.thumbnailSplashProgress.value = 0.5f
+ taskThumbnailViewData.width.value = THUMBNAIL_WIDTH * 2
+ taskThumbnailViewData.height.value = THUMBNAIL_HEIGHT * 2
+
+ assertThat(systemUnderTest.execute(2).first()).isEqualTo(0.5f)
+ }
+
+ @Test
+ fun execute_withDifferentAspectRatioAndRotation_showsSplash() = runTest {
+ setupTask(2, createThumbnailData(rotation = ROTATION_90))
+ recentsViewData.thumbnailSplashProgress.value = 0.5f
+ taskThumbnailViewData.width.value = THUMBNAIL_WIDTH
+ taskThumbnailViewData.height.value = THUMBNAIL_HEIGHT * 2
+
+ assertThat(systemUnderTest.execute(2).first()).isEqualTo(0.5f)
+ }
+
+ private val tasks = (0..5).map(::createTaskWithId)
+
+ private fun setupTask(taskId: Int, thumbnailData: ThumbnailData = createThumbnailData()) {
+ recentTasksRepository.seedThumbnailData(mapOf(taskId to thumbnailData))
+ val expectedIconData = createIconData("Task $taskId")
+ recentTasksRepository.seedIconData(mapOf(taskId to expectedIconData))
+ recentTasksRepository.seedTasks(tasks)
+ recentTasksRepository.setVisibleTasks(listOf(taskId))
+ }
+
+ private fun createThumbnailData(
+ rotation: Int = Surface.ROTATION_0,
+ width: Int = THUMBNAIL_WIDTH,
+ height: Int = THUMBNAIL_HEIGHT
+ ): ThumbnailData {
+ val bitmap = mock<Bitmap>()
+ whenever(bitmap.width).thenReturn(width)
+ whenever(bitmap.height).thenReturn(height)
+
+ return ThumbnailData(thumbnail = bitmap, rotation = rotation)
+ }
+
+ private fun createIconData(title: String) = TaskIconQueryResponse(mock<Drawable>(), "", title)
+
+ private fun createTaskWithId(taskId: Int) =
+ Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+ colorBackground = Color.argb(taskId, taskId, taskId, taskId)
+ }
+
+ companion object {
+ const val THUMBNAIL_WIDTH = 100
+ const val THUMBNAIL_HEIGHT = 200
+
+ const val SPLASH_HIDDEN = 0f
+ const val SPLASH_SHOWN = 1f
+ }
+}
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 754c9d1..fe7d37a 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,8 +21,12 @@
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
import com.android.quickstep.recents.data.FakeTasksRepository
+import com.android.quickstep.recents.data.TaskIconQueryResponse
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
import com.android.quickstep.recents.usecase.ThumbnailPositionState.MissingThumbnail
@@ -30,6 +34,8 @@
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.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskThumbnailViewModel
@@ -40,11 +46,14 @@
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
+/** Test for [TaskThumbnailView] */
@RunWith(AndroidJUnit4::class)
class TaskThumbnailViewModelTest {
private var taskViewType = TaskViewType.SINGLE
@@ -53,18 +62,27 @@
private val taskContainerData = TaskContainerData()
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,
taskViewData,
taskContainerData,
tasksRepository,
- mGetThumbnailPositionUseCase
+ 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)
@@ -72,14 +90,42 @@
@Test
fun bindRunningTask_thenStateIs_LiveTile() = runTest {
+ val taskId = 1
tasksRepository.seedTasks(tasks)
- val taskThumbnail = TaskThumbnail(taskId = 1, isRunning = true)
- systemUnderTest.bind(taskThumbnail)
+ tasksRepository.setVisibleTasks(listOf(taskId))
+ recentsViewData.runningTaskIds.value = setOf(taskId)
+ systemUnderTest.bind(taskId)
assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
}
@Test
+ fun bindRunningTaskShouldShowScreenshot_thenStateIs_SnapshotSplash() = runTest {
+ val taskId = 1
+ val expectedThumbnailData = createThumbnailData()
+ tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
+ val expectedIconData = createIconData("Task 1")
+ tasksRepository.seedIconData(mapOf(taskId to expectedIconData))
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(listOf(taskId))
+ recentsViewData.runningTaskIds.value = setOf(taskId)
+ recentsViewData.runningTaskShowScreenshot.value = true
+ systemUnderTest.bind(taskId)
+
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ SnapshotSplash(
+ Snapshot(
+ backgroundColor = Color.rgb(1, 1, 1),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_0,
+ ),
+ Splash(expectedIconData.icon, Point())
+ )
+ )
+ }
+
+ @Test
fun setRecentsFullscreenProgress_thenCornerRadiusProgressIsPassedThrough() = runTest {
recentsViewData.fullscreenProgress.value = 0.5f
@@ -113,87 +159,119 @@
@Test
fun bindRunningTaskThenStoppedTaskWithoutThumbnail_thenStateChangesToBackgroundOnly() =
runTest {
+ val runningTaskId = 1
+ val stoppedTaskId = 2
tasksRepository.seedTasks(tasks)
- val runningTask = TaskThumbnail(taskId = 1, isRunning = true)
- val stoppedTask = TaskThumbnail(taskId = 2, isRunning = false)
- systemUnderTest.bind(runningTask)
+ tasksRepository.setVisibleTasks(listOf(runningTaskId, stoppedTaskId))
+ recentsViewData.runningTaskIds.value = setOf(runningTaskId)
+ systemUnderTest.bind(runningTaskId)
assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
- systemUnderTest.bind(stoppedTask)
+ systemUnderTest.bind(stoppedTaskId)
assertThat(systemUnderTest.uiState.first())
.isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
}
@Test
fun bindStoppedTaskWithoutThumbnail_thenStateIs_BackgroundOnly_withAlphaRemoved() = runTest {
+ val stoppedTaskId = 2
tasksRepository.seedTasks(tasks)
- val stoppedTask = TaskThumbnail(taskId = 2, isRunning = false)
+ tasksRepository.setVisibleTasks(listOf(stoppedTaskId))
- systemUnderTest.bind(stoppedTask)
+ systemUnderTest.bind(stoppedTaskId)
assertThat(systemUnderTest.uiState.first())
.isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
}
@Test
fun bindLockedTaskWithThumbnail_thenStateIs_BackgroundOnly() = runTest {
- tasksRepository.seedThumbnailData(mapOf(2 to createThumbnailData()))
- tasks[2].isLocked = true
+ val taskId = 2
+ tasksRepository.seedThumbnailData(mapOf(taskId to createThumbnailData()))
+ tasks[taskId].isLocked = true
tasksRepository.seedTasks(tasks)
- val recentTask = TaskThumbnail(taskId = 2, isRunning = false)
+ tasksRepository.setVisibleTasks(listOf(taskId))
- systemUnderTest.bind(recentTask)
+ systemUnderTest.bind(taskId)
assertThat(systemUnderTest.uiState.first())
.isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
}
@Test
- fun bindStoppedTaskWithThumbnail_thenStateIs_Snapshot_withAlphaRemoved() = runTest {
- val expectedThumbnailData = createThumbnailData()
- tasksRepository.seedThumbnailData(mapOf(2 to expectedThumbnailData))
+ fun bindStoppedTaskWithThumbnail_thenStateIs_SnapshotSplash_withAlphaRemoved() = runTest {
+ val taskId = 2
+ 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(2))
- val recentTask = TaskThumbnail(taskId = 2, isRunning = false)
+ tasksRepository.setVisibleTasks(listOf(taskId))
- systemUnderTest.bind(recentTask)
+ systemUnderTest.bind(taskId)
assertThat(systemUnderTest.uiState.first())
.isEqualTo(
- Snapshot(
- backgroundColor = Color.rgb(2, 2, 2),
- bitmap = expectedThumbnailData.thumbnail!!,
+ SnapshotSplash(
+ Snapshot(
+ backgroundColor = Color.rgb(2, 2, 2),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_270,
+ ),
+ Splash(expectedIconData.icon, Point())
)
)
}
@Test
- fun bindNonVisibleStoppedTask_whenMadeVisible_thenStateIsSnapshot() = runTest {
+ fun bindNonVisibleStoppedTask_whenMadeVisible_thenStateIsSnapshotSplash() = runTest {
+ val taskId = 2
val expectedThumbnailData = createThumbnailData()
- tasksRepository.seedThumbnailData(mapOf(2 to expectedThumbnailData))
+ tasksRepository.seedThumbnailData(mapOf(taskId to expectedThumbnailData))
+ val expectedIconData = createIconData("Task 2")
+ tasksRepository.seedIconData(mapOf(taskId to expectedIconData))
tasksRepository.seedTasks(tasks)
- val recentTask = TaskThumbnail(taskId = 2, isRunning = false)
- systemUnderTest.bind(recentTask)
- assertThat(systemUnderTest.uiState.first())
- .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
- tasksRepository.setVisibleTasks(listOf(2))
+ systemUnderTest.bind(taskId)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
+
+ tasksRepository.setVisibleTasks(listOf(taskId))
assertThat(systemUnderTest.uiState.first())
.isEqualTo(
- Snapshot(
- backgroundColor = Color.rgb(2, 2, 2),
- bitmap = expectedThumbnailData.thumbnail!!,
+ SnapshotSplash(
+ Snapshot(
+ backgroundColor = Color.rgb(2, 2, 2),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ thumbnailRotation = Surface.ROTATION_0,
+ ),
+ Splash(expectedIconData.icon, Point())
)
)
}
@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 recentTask = TaskThumbnail(taskId = taskId, isRunning = false)
val isRtl = true
whenever(mGetThumbnailPositionUseCase.run(taskId, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
.thenReturn(MissingThumbnail)
- systemUnderTest.bind(recentTask)
+ systemUnderTest.bind(taskId)
assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
.isEqualTo(Matrix.IDENTITY_MATRIX)
}
@@ -201,30 +279,52 @@
@Test
fun getSnapshotMatrix_MatrixScaling() = runTest {
val taskId = 2
- val recentTask = TaskThumbnail(taskId = taskId, isRunning = false)
val isRtl = true
whenever(mGetThumbnailPositionUseCase.run(taskId, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
.thenReturn(MatrixScaling(MATRIX, isRotated = false))
- systemUnderTest.bind(recentTask)
+ systemUnderTest.bind(taskId)
assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
.isEqualTo(MATRIX)
}
+ @Test
+ fun getForegroundScrimDimProgress_returnsForegroundMaxScrim() = runTest {
+ recentsViewData.tintAmount.value = 0.32f
+ taskContainerData.taskMenuOpenProgress.value = 0f
+ assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0.32f)
+ }
+
+ @Test
+ fun getTaskMenuScrimDimProgress_returnsTaskMenuScrim() = runTest {
+ recentsViewData.tintAmount.value = 0f
+ taskContainerData.taskMenuOpenProgress.value = 1f
+ assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0.4f)
+ }
+
+ @Test
+ fun getForegroundScrimDimProgress_returnsNoScrim() = runTest {
+ recentsViewData.tintAmount.value = 0f
+ taskContainerData.taskMenuOpenProgress.value = 0f
+ assertThat(systemUnderTest.dimProgress.first()).isEqualTo(0f)
+ }
+
private fun createTaskWithId(taskId: Int) =
Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
colorBackground = Color.argb(taskId, taskId, taskId, taskId)
}
- private fun createThumbnailData(): ThumbnailData {
+ private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): ThumbnailData {
val bitmap = mock<Bitmap>()
whenever(bitmap.width).thenReturn(THUMBNAIL_WIDTH)
whenever(bitmap.height).thenReturn(THUMBNAIL_HEIGHT)
- return ThumbnailData(thumbnail = bitmap)
+ return ThumbnailData(thumbnail = bitmap, rotation = rotation)
}
+ private fun createIconData(title: String) = TaskIconQueryResponse(mock<Drawable>(), "", title)
+
companion object {
const val THUMBNAIL_WIDTH = 100
const val THUMBNAIL_HEIGHT = 200
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TestExtensions.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TestExtensions.kt
new file mode 100644
index 0000000..6c526a4
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TestExtensions.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.BuildConfig
+import com.android.launcher3.util.SafeCloseable
+import com.android.quickstep.DeviceConfigWrapper.Companion.configHelper
+import com.android.quickstep.util.DeviceConfigHelper.Companion.prefs
+import java.util.concurrent.CountDownLatch
+import java.util.function.BooleanSupplier
+import org.junit.Assert
+import org.junit.Assume
+
+/** Helper methods for testing */
+object TestExtensions {
+
+ @JvmStatic
+ fun overrideNavConfigFlag(
+ key: String,
+ value: Boolean,
+ targetValue: BooleanSupplier
+ ): AutoCloseable {
+ Assume.assumeTrue(BuildConfig.IS_DEBUG_DEVICE)
+ if (targetValue.asBoolean == value) {
+ return AutoCloseable {}
+ }
+
+ navConfigEditWatcher().let {
+ prefs.edit().putBoolean(key, value).commit()
+ it.close()
+ }
+ Assert.assertEquals(value, targetValue.asBoolean)
+
+ val watcher = navConfigEditWatcher()
+ return AutoCloseable {
+ prefs.edit().remove(key).commit()
+ watcher.close()
+ }
+ }
+
+ private fun navConfigEditWatcher(): SafeCloseable {
+ val wait = CountDownLatch(1)
+ val listener = Runnable { wait.countDown() }
+ configHelper.addChangeListener(listener)
+
+ return SafeCloseable {
+ wait.await()
+ configHelper.removeChangeListener(listener)
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index 27e761a..88ffeea 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -18,10 +18,14 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
+import android.content.res.Resources
import android.os.Process
import android.os.UserHandle
+import android.platform.test.rule.TestWatcher
import android.testing.AndroidTestingRunner
+import com.android.internal.R
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
import com.android.launcher3.model.data.AppInfo
@@ -39,12 +43,14 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.Description
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -53,32 +59,65 @@
class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
@get:Rule val mockitoRule = MockitoJUnit.rule()
+ @get:Rule
+ val disableControllerForCertainTestsWatcher =
+ object : TestWatcher() {
+ override fun starting(description: Description) {
+ // Update canShowRunningAndRecentAppsAtInit before setUp() is called for each test.
+ canShowRunningAndRecentAppsAtInit =
+ description.methodName !in
+ listOf(
+ "canShowRunningAndRecentAppsAtInitIsFalse_getTasksNeverCalled",
+ )
+ }
+ }
@Mock private lateinit var mockIconCache: TaskIconCache
@Mock private lateinit var mockRecentsModel: RecentsModel
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockResources: Resources
@Mock private lateinit var mockDesktopVisibilityController: DesktopVisibilityController
private var taskListChangeId: Int = 1
private lateinit var recentAppsController: TaskbarRecentAppsController
- private lateinit var recentTasksChangedListener: RecentTasksChangedListener
private lateinit var userHandle: UserHandle
+ private var canShowRunningAndRecentAppsAtInit = true
+ private var recentTasksChangedListener: RecentTasksChangedListener? = null
+
@Before
fun setUp() {
super.setup()
userHandle = Process.myUserHandle()
- whenever(mockRecentsModel.iconCache).thenReturn(mockIconCache)
- recentAppsController =
- TaskbarRecentAppsController(mockRecentsModel) { mockDesktopVisibilityController }
- recentAppsController.init(taskbarControllers)
- recentAppsController.canShowRunningApps = true
- recentAppsController.canShowRecentApps = true
+ // Set desktop mode supported
+ whenever(mockContext.getResources()).thenReturn(mockResources)
+ whenever(mockResources.getBoolean(R.bool.config_isDesktopModeSupported)).thenReturn(true)
- val listenerCaptor = ArgumentCaptor.forClass(RecentTasksChangedListener::class.java)
- verify(mockRecentsModel).registerRecentTasksChangedListener(listenerCaptor.capture())
- recentTasksChangedListener = listenerCaptor.value
+ whenever(mockRecentsModel.iconCache).thenReturn(mockIconCache)
+ whenever(mockRecentsModel.unregisterRecentTasksChangedListener()).then {
+ recentTasksChangedListener = null
+ it
+ }
+ recentAppsController =
+ TaskbarRecentAppsController(mockContext, mockRecentsModel) {
+ mockDesktopVisibilityController
+ }
+ recentAppsController.canShowRunningApps = canShowRunningAndRecentAppsAtInit
+ recentAppsController.canShowRecentApps = canShowRunningAndRecentAppsAtInit
+ recentAppsController.init(taskbarControllers)
+
+ recentTasksChangedListener =
+ if (canShowRunningAndRecentAppsAtInit) {
+ val listenerCaptor = ArgumentCaptor.forClass(RecentTasksChangedListener::class.java)
+ verify(mockRecentsModel)
+ .registerRecentTasksChangedListener(listenerCaptor.capture())
+ listenerCaptor.value
+ } else {
+ verify(mockRecentsModel, never()).registerRecentTasksChangedListener(any())
+ null
+ }
// Make sure updateHotseatItemInfos() is called after commitRunningAppsToUI()
whenever(taskbarViewController.commitRunningAppsToUI()).then {
@@ -88,6 +127,32 @@
}
}
+ // See the TestWatcher rule at the top which sets canShowRunningAndRecentAppsAtInit = false.
+ @Test
+ fun canShowRunningAndRecentAppsAtInitIsFalse_getTasksNeverCalled() {
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
+ runningTasks = listOf(createTask(1, RUNNING_APP_PACKAGE_1)),
+ recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
+ )
+ verify(mockRecentsModel, never()).getTasks(any<Consumer<List<GroupTask>>>())
+ }
+
+ @Test
+ fun canShowRunningAndRecentAppsIsFalseAfterInit_getTasksOnlyCalledInInit() {
+ // getTasks() should have been called once from init().
+ verify(mockRecentsModel, times(1)).getTasks(any<Consumer<List<GroupTask>>>())
+ recentAppsController.canShowRunningApps = false
+ recentAppsController.canShowRecentApps = false
+ prepareHotseatAndRunningAndRecentApps(
+ hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2),
+ runningTasks = listOf(createTask(1, RUNNING_APP_PACKAGE_1)),
+ recentTaskPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
+ )
+ // Verify that getTasks() was not called again after the init().
+ verify(mockRecentsModel, times(1)).getTasks(any<Consumer<List<GroupTask>>>())
+ }
+
@Test
fun updateHotseatItemInfos_cantShowRunning_inDesktopMode_returnsAllHotseatItems() {
recentAppsController.canShowRunningApps = false
@@ -518,7 +583,7 @@
)
setInDesktopMode(true)
- recentTasksChangedListener.onRecentTasksChanged()
+ recentTasksChangedListener!!.onRecentTasksChanged()
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
assertThat(shownPackages).containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
}
@@ -535,7 +600,7 @@
recentTaskPackages = recentTaskPackages
)
setInDesktopMode(false)
- recentTasksChangedListener.onRecentTasksChanged()
+ recentTasksChangedListener!!.onRecentTasksChanged()
val shownPackages = recentAppsController.shownTasks.flatMap { it.packageNames }
// Don't expect RECENT_PACKAGE_3 because it is currently running.
val expectedPackages = listOf(RECENT_PACKAGE_1, RECENT_PACKAGE_2)
@@ -705,7 +770,7 @@
}
.whenever(mockRecentsModel)
.getTasks(any<Consumer<List<GroupTask>>>())
- recentTasksChangedListener.onRecentTasksChanged()
+ recentTasksChangedListener?.onRecentTasksChanged()
}
private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index d049fbc..cbc8441 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -29,9 +29,12 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.res.Resources;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.launcher3.util.LooperExecutor;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
@@ -54,7 +57,11 @@
public class RecentTasksListTest {
@Mock
- private SystemUiProxy mockSystemUiProxy;
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private SystemUiProxy mSystemUiProxy;
@Mock
private TopTaskTracker mTopTaskTracker;
@@ -66,14 +73,19 @@
MockitoAnnotations.initMocks(this);
LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
KeyguardManager mockKeyguardManager = mock(KeyguardManager.class);
- mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManager,
- mockSystemUiProxy, mTopTaskTracker);
+
+ // Set desktop mode supported
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getBoolean(R.bool.config_isDesktopModeSupported)).thenReturn(true);
+
+ mRecentTasksList = new RecentTasksList(mContext, mockMainThreadExecutor,
+ mockKeyguardManager, mSystemUiProxy, mTopTaskTracker);
}
@Test
public void onRecentTasksChanged_doesNotFetchTasks() throws Exception {
mRecentTasksList.onRecentTasksChanged();
- verify(mockSystemUiProxy, times(0))
+ verify(mSystemUiProxy, times(0))
.getRecentTasks(anyInt(), anyInt());
}
@@ -81,7 +93,7 @@
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() throws Exception {
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(
new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
@@ -94,7 +106,7 @@
@Test
public void loadTasksInBackground_GetRecentTasksException() throws Exception {
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenThrow(new SystemUiProxy.GetRecentTasksException("task load failed"));
RecentTasksList.TaskLoadResult taskList = mRecentTasksList.loadTasksInBackground(
@@ -113,7 +125,7 @@
task2.taskDescription = new ActivityManager.TaskDescription();
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(task1, task2,
null);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
@@ -132,7 +144,7 @@
createRecentTaskInfo(5 /* taskId */)};
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forFreeformTasks(
tasks, Collections.emptySet() /* minimizedTaskIds */);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(
@@ -158,7 +170,7 @@
Arrays.stream(new Integer[]{1, 4, 5}).collect(Collectors.toSet());
GroupedRecentTaskInfo recentTaskInfos =
GroupedRecentTaskInfo.forFreeformTasks(tasks, minimizedTaskIds);
- when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(
diff --git a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 2d79623..3a83ae3 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -16,7 +16,7 @@
package com.android.quickstep;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@@ -71,7 +71,7 @@
final ArgumentCaptor<ActivityOptions> optionsCaptor =
ArgumentCaptor.forClass(ActivityOptions.class);
verify(mSystemUiProxy).startRecentsActivity(any(), optionsCaptor.capture(), any());
- assertTrue(optionsCaptor.getValue()
- .isPendingIntentBackgroundActivityLaunchAllowedByPermission());
+ assertEquals(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS,
+ optionsCaptor.getValue().getPendingIntentBackgroundActivityStartMode());
}
}
diff --git a/res/drawable/rounded_action_button.xml b/res/drawable/rounded_action_button.xml
index ddd3042..ebfa996 100644
--- a/res/drawable/rounded_action_button.xml
+++ b/res/drawable/rounded_action_button.xml
@@ -22,8 +22,5 @@
<stroke
android:width="1dp"
android:color="?attr/materialColorSurfaceContainerLow" />
- <padding
- android:left="@dimen/rounded_button_padding"
- android:right="@dimen/rounded_button_padding" />
</shape>
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index c581ae3..a45d585 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -44,8 +44,7 @@
<FrameLayout
android:layout_width="@dimen/rounded_button_width"
android:layout_height="@dimen/rounded_button_width"
- android:background="@drawable/rounded_action_button"
- android:padding="@dimen/rounded_button_padding">
+ android:background="@drawable/rounded_action_button">
<ImageButton
android:id="@+id/action_btn"
android:layout_width="@dimen/x_icon_size"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 1a8f8e2..dca7cab 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Apppaar is nie beskikbaar nie"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Raak en hou om \'n legstuk te skuif."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dubbeltik en hou om \'n legstuk te skuif of gebruik gepasmaakte handelinge."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Meer opsies"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Wys alle legstukke"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 80447c5..3e0eaf7 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 4eee121..794c249 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 52ec7ea..55a80b4 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -38,6 +38,8 @@
<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="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 বহল x %2$d ওখ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেট"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 6c1cc46..7713e89 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Tətbiq cütü əlçatan deyil"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Vidceti daşımaq üçün toxunub saxlayın."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Vidceti daşımaq üçün iki dəfə toxunub saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Digər seçimlər"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Bütün vidcetləri göstərin"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidceti"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 24328cf..71bd34c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Par aplikacija nije dostupan"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite radi pomeranja vidžeta."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da biste pomerali vidžet ili koristite prilagođene radnje."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Još opcija"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Prikaži sve vidžete"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidžet"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index ebbb378..edb9a35 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index d5d948e..3d39828 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index cf75fb58..09ebf2f 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -38,6 +38,8 @@
<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="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">"%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>টি উইজেট"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 1758c39..346a7ea 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Par aplikacija nije dostupan"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite da pomjerite vidžet."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da pomjerite vidžet ili da koristite prilagođene radnje."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Više opcija"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Prikazuj sve vidžete"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index bf578f5..9da16bb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"La parella d\'aplicacions no està disponible"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Fes doble toc i mantén premut per moure un widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Fes doble toc i mantén premut per moure un widget o per utilitzar accions personalitzades."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Més opcions"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostra tots els widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget de <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 9a8fc6f..63858d3 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Dvojice aplikací není k dispozici"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Widget přesunete klepnutím a podržením."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvojitým klepnutím a podržením přesunete widget, případně použijte vlastní akce."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Další možnosti"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Zobrazit všechny widgety"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 208c2ef..99b1bf2 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Appsammenknytning er ikke tilgængelig"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Hold en widget nede for at flytte den."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tryk to gange, og hold en widget nede for at flytte den eller bruge tilpassede handlinger."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Flere valgmuligheder"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Vis alle widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widgetten <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 380030b..d052fba 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App-Paar nicht verfügbar"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Zum Verschieben des Widgets gedrückt halten"</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Doppeltippen und halten, um ein Widget zu bewegen oder benutzerdefinierte Aktionen zu nutzen."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Weitere Optionen"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Alle Widgets anzeigen"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breit und %2$d hoch"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index e86ebae..5a178a4 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 50c5976..c9579a5 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App pair isn\'t available"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap & hold to move a widget or use custom actions."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"More options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Show all widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 08ff6e7..311b4b9 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App pair isn\'t available"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap and hold to move a widget or use custom actions."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"More options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Show all widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 50c5976..c9579a5 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App pair isn\'t available"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap & hold to move a widget or use custom actions."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"More options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Show all widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 50c5976..c9579a5 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App pair isn\'t available"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap & hold to move a widget or use custom actions."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"More options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Show all widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index fa6d1f1..047a67f 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App pair isn\'t available"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch & hold to move a widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap & hold to move a widget or use custom actions."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"More options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Show all widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 5879129..f5b4d8a 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"La vinculación de apps no está disponible"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Mantén presionado para mover un widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Presiona dos veces y mantén presionado para mover un widget o usar acciones personalizadas."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Más opciones"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostrar todos los widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 8d384ac..92e4a2d0 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"La aplicación emparejada no está disponible"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Mantén pulsado un widget para moverlo"</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toca dos veces y mantén pulsado un widget para moverlo o usar acciones personalizadas."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Más opciones"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostrar todos los widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget de <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 44448a6..c2be8cd 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Rakendusepaar ei ole saadaval"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Vidina teisaldamiseks puudutage ja hoidke all."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Vidina teisaldamiseks või kohandatud toimingute kasutamiseks topeltpuudutage ja hoidke all."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Rohkem valikuid"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Kuva kõik vidinad"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 6fc4cf4..4a9ae90 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Aplikazio parea ez dago erabilgarri"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Eduki sakatuta widget bat mugitzeko."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Sakatu birritan eta eduki sakatuta widget bat mugitzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Aukera gehiago"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Erakutsi widget guztiak"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index fffce13..0047f8d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 310053e..9eaaeb6 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Sovelluspari ei ole saatavilla"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Kosketa pitkään, niin voit siirtää widgetiä."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Kaksoisnapauta ja paina pitkään, niin voit siirtää widgetiä tai käyttää muokattuja toimintoja."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Lisäasetukset"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Näytä kaikki widgetit"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 11b2c01..5490ee3 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"La Paire d\'applis n\'est pas offerte"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Maintenez le doigt sur un widget pour le déplacer."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Touchez 2x un widget et maintenez le doigt dessus pour le déplacer ou utiliser des actions personnalisées."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Autres options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Afficher tous les widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b4c5ec6..6e04abd 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"La paire d\'applications n\'est pas disponible"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Appuyez de manière prolongée sur un widget pour le déplacer."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Appuyez deux fois et maintenez la pression pour déplacer widget ou utiliser actions personnalisées."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Autres options"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Afficher tous les widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index ac72802..e68d0e5 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Non está dispoñible o emparellamento de aplicacións"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Mantén premido un widget para movelo."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toca dúas veces un widget e manteno premido para movelo ou utiliza accións personalizadas."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Máis opcións"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostrar todos os widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 74747d0..92d52a7 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -38,6 +38,8 @@
<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="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 પહોળાઈ X %2$d ઊંચાઈ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 6071935..79fcc3f 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index cf7a91a..eaf1b55 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Par aplikacija nije dostupan"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite da biste premjestili widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite pritisak da biste premjestili widget ili upotrijebite prilagođene radnje"</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Više opcija"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Prikaži sve widgete"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index f306110..c33cb5a 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Az alkalmazáspár nem áll rendelkezésre"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Tartsa lenyomva a modult az áthelyezéshez."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Modul áthelyezéséhez koppintson duplán, tartsa nyomva az ujját, vagy használjon egyéni műveleteket."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"További lehetőségek"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Minden modul mutatása"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 2d345f2..f06ff83 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 9ced9f4..8487e62 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Pasangan aplikasi tidak tersedia"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Sentuh lama untuk memindahkan widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ketuk dua kali & tahan untuk memindahkan widget atau gunakan tindakan khusus."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Opsi lainnya"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Tampilkan semua widget"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 2ab7817..ab343e5 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Forritapar er ekki í boði"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Haltu fingri á græju til að færa hana."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ýttu tvisvar og haltu fingri á græju til að færa hana eða notaðu sérsniðnar aðgerðir."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Fleiri valkostir"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Sýna allar græjur"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Græjan <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a59de6c..378d1e9 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"La coppia di app non è disponibile"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Tocca e tieni premuto per spostare un widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tocca due volte e tieni premuto per spostare un widget o per usare le azioni personalizzate."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Altre opzioni"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostra tutti i widget"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 89a0c8a..2e8cacb 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index b48c7be..e0aa65f 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -38,6 +38,8 @@
<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="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$dx%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>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 76b8b5d..1d674cb 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 95d4420..363fc2f 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 5c71276..ab5decf 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 931ca30..04daaac 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 9f64cfb..7aa9e03 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 3e67c36..d0079d0 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index e0cd3ee..9572f3d 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 9846525..6cd8872 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Programų pora nepasiekiama"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Dukart pal. ir palaik., kad perkeltumėte valdiklį."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dukart palieskite ir palaikykite, kad perkeltumėte valdiklį ar naudotumėte tinkintus veiksmus."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Daugiau parinkčių"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Rodyti visus valdiklius"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> valdiklis"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 49f7ffe..426fdc0 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Lietotņu pāris nav pieejams"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Lai pārvietotu logrīku, pieskarieties un turiet."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Lai pārvietotu logrīku, uz tā veiciet dubultskārienu un turiet. Varat arī veikt pielāgotas darbības."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Citas iespējas"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Rādīt visus logrīkus"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Logrīks <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 2bfa089..c84f232 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index a2babd5..ff06bcf 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 93a1c9c..d9b220c 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index c084fab..2f78245 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 72d92e2..d2fd7a8 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Gandingan apl tidak tersedia"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Sentuh & tahan untuk menggerakkan widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ketik dua kali & tahan untuk menggerakkan widget atau menggunakan tindakan tersuai."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Lagi pilihan"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Tunjukkan semua widget"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 194ece7..946d8df 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index a9e6c5d..5c0158c 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Apptilkoblingen er ikke tilgjengelig"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Trykk og hold for å flytte en modul."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dobbelttrykk og hold inne for å flytte en modul eller bruke tilpassede handlinger."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Flere alternativer"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Vis alle moduler"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modul"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 7880c36..eddcefb 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 7b3f563..3402086 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App-paar is niet beschikbaar"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Tik en houd vast om een widget te verplaatsen."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dubbeltik en houd vast om een widget te verplaatsen of aangepaste acties te gebruiken."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Meer opties"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Alle widgets tonen"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 33b7664..74001c7 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 71c25eb..e72d1f3 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 587c40f..76ddbdc 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Para aplikacji jest niedostępna"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Naciśnij i przytrzymaj, aby przenieść widżet."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Naciśnij dwukrotnie i przytrzymaj, aby przenieść widżet lub użyć działań niestandardowych."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Więcej opcji"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Pokaż wszystkie widżety"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 67d93de..1f98805 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"O par de apps não está disponível"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque sem soltar para mover um widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes sem soltar para mover um widget ou utilizar ações personalizadas."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Mais opções"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostrar todos os widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 4302588..20edc93 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"O Par de apps não está disponível"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque e pressione para mover um widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes e mantenha a tela pressionada para mover um widget ou usar ações personalizadas."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Mais opções"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Mostrar todos os widgets"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index d9e0413..ec8e558 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Perechea de aplicații nu este disponibilă"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Atinge și ține apăsat pentru a muta un widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Atinge de două ori și ține apăsat pentru a muta un widget sau folosește acțiuni personalizate."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Mai multe opțiuni"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Afișează toate widgeturile"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 64a21f7..c941cba 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -38,6 +38,8 @@
<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="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 x %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>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 71efc03..298a078 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 43bc29c..94d109c 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Pár aplikácií nie je k dispozícii"</string>
<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_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>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 5d423ca..6ee5e07 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Par aplikacij ni na voljo"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Pridržite pripomoček, da ga premaknete."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvakrat se dotaknite pripomočka in ga pridržite, da ga premaknete, ali pa uporabite dejanja po meri."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Več možnosti"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Pokaži vse pripomočke"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Pripomoček <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 8f4133d..f106fab 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Çifti i aplikacioneve nuk ofrohet"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Prek dhe mbaj shtypur një miniaplikacion për ta zhvendosur."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Trokit dy herë dhe mbaje shtypur një miniapliikacion për ta zhvendosur atë ose për të përdorur veprimet e personalizuara."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Opsione të tjera"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Shfaq të gjitha miniapl."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> miniaplikacion"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index cd6523c..a9c4e24 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 4f95c67..5806d1e 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"App-paret är inte tillgängligt"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Tryck länge för att flytta en widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tryck snabbt två gånger och håll kvar för att flytta en widget eller använda anpassade åtgärder."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Fler alternativ"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Visa alla widgetar"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget för <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 53a9abe..5d69573 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Kipengele cha jozi ya programu hakipatikani"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Gusa na ushikilie ili usogeze wijeti."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Gusa mara mbili na ushikilie ili usogeze wijeti au utumie vitendo maalum."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Chaguo zaidi"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Onyesha wijeti zote"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 738f85c..559843a 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 0520ebf..cf18156 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -38,6 +38,8 @@
<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="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 వెడల్పు X %2$d ఎత్తు"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 554fd94..706c4f4 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -38,6 +38,8 @@
<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="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 x สูง %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"วิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index cb6fe66..d99ffdc 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Hindi available ang pares ng app"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Pindutin nang matagal para ilipat ang widget."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"I-double tap at pindutin nang matagal para ilipat ang widget o gumamit ng mga custom na pagkilos."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Higit pang opsyon"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Ipakita lahat ng widget"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index b12ec27..09939b3 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Uygulama çifti kullanılamıyor"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Widget\'ı taşımak için dokunup basılı tutun."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Widget\'ı taşımak veya özel işlemleri kullanmak için iki kez dokunup basılı tutun."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Diğer seçenekler"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Tüm widget\'ları göster"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 2a01dae..0c9ee22 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 544357e..8312ece 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 5038d4f..8f41d49 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Ikkita ilovadan bir vaqtda foydalanish mumkin emas"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Vidjetni bosib turgan holatda suring."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Yana"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Barcha vidjetlar"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ta vidjet"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index e5252b1..af4a773 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Hiện không có cặp ứng dụng này"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Chạm và giữ để di chuyển một tiện ích."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Nhấn đúp và giữ để di chuyển một tiện ích hoặc sử dụng các thao tác tùy chỉnh."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Lựa chọn khác"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Hiện tất cả tiện ích"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7a76158..3435eb0 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index ed6e52f..950d54b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -27,7 +27,7 @@
<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>
+ <string name="set_default_home_app" msgid="5808906607627586381">"在「設定」中將「<xliff:g id="LAUNCHER_NAME">%1$s</xliff:g>」設定為預設主頁應用程式"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割螢幕"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的應用程式資料"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"「%1$s」的用量設定"</string>
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 264d607..331071d 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -38,6 +38,8 @@
<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="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>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index be6cd49..d167f14 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -38,6 +38,8 @@
<string name="app_pair_not_available" msgid="3556767440808032031">"Ukubhangqwa kwe-app akutholakali"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Thinta uphinde ubambe ukuze uhambise iwijethi."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Thepha kabili uphinde ubambe ukuze uhambise iwijethi noma usebenzise izindlela ezingokwezifiso."</string>
+ <string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Okungakhethwa kukho okuningi"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Bonisa wonke amawijethi"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Iwijethi elingu-<xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 05724e2..af91b5a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -172,8 +172,6 @@
<dimen name="padded_rounded_button_height">48dp</dimen>
<dimen name="rounded_button_height">48dp</dimen>
<dimen name="rounded_button_radius">200dp</dimen>
- <dimen name="rounded_button_padding">8dp</dimen>
-
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 00db3a3..1a5f4b9 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -993,7 +993,6 @@
return mHotseatColumnSpan;
}
- @VisibleForTesting
public int getHotseatWidthPx() {
return mHotseatWidthPx;
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index f775673..0d4ebe0 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -162,14 +162,14 @@
animatorSet.play(ObjectAnimator.ofFloat(child, VIEW_TRANSLATE_X, tx));
}
}
- if (mQsb instanceof HorizontalInsettableView) {
- HorizontalInsettableView horizontalInsettableQsb = (HorizontalInsettableView) mQsb;
- ValueAnimator qsbAnimator = ValueAnimator.ofFloat(0f, 1f);
+ if (mQsb instanceof HorizontalInsettableView horizontalInsettableQsb) {
+ final float currentInsetFraction = horizontalInsettableQsb.getHorizontalInsets();
+ final float targetInsetFraction =
+ isBubbleBarVisible ? (float) dp.iconSizePx / dp.hotseatQsbWidth : 0;
+ ValueAnimator qsbAnimator =
+ ValueAnimator.ofFloat(currentInsetFraction, targetInsetFraction);
qsbAnimator.addUpdateListener(animation -> {
- float fraction = qsbAnimator.getAnimatedFraction();
- float insetFraction = isBubbleBarVisible
- ? (float) dp.iconSizePx * fraction / dp.hotseatQsbWidth
- : (float) dp.iconSizePx * (1 - fraction) / dp.hotseatQsbWidth;
+ float insetFraction = (float) animation.getAnimatedValue();
horizontalInsettableQsb.setHorizontalInsets(insetFraction);
});
animatorSet.play(qsbAnimator);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5949732..6c706be 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -266,6 +266,7 @@
import com.android.launcher3.widget.custom.CustomWidgetManager;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.plugins.LauncherOverlayPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -371,6 +372,7 @@
private LauncherAccessibilityDelegate mAccessibilityDelegate;
private PopupDataProvider mPopupDataProvider;
+ private WidgetPickerDataProvider mWidgetPickerDataProvider;
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -532,6 +534,7 @@
mFocusHandler, new CellLayout(mWorkspace.getContext(), mWorkspace));
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
+ mWidgetPickerDataProvider = new WidgetPickerDataProvider();
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
if (internalStateHandled) {
@@ -2702,6 +2705,7 @@
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
mPopupDataProvider.dump(prefix, writer);
+ mWidgetPickerDataProvider.dump(prefix, writer);
mDeviceProfile.dump(this, prefix, writer);
mAppsView.getAppsStore().dump(prefix, writer);
@@ -3011,6 +3015,12 @@
return mPopupDataProvider;
}
+ @NonNull
+ @Override
+ public WidgetPickerDataProvider getWidgetPickerDataProvider() {
+ return mWidgetPickerDataProvider;
+ }
+
@Override
public DotInfo getDotInfoForItem(ItemInfo info) {
return mPopupDataProvider.getDotInfoForItem(info);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index b41da0f..08ccfb2 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -115,9 +115,7 @@
if (BuildCompat.isAtLeastV() && Flags.enableSupportForArchiving()) {
ArchiveCompatibilityParams params = new ArchiveCompatibilityParams();
params.setEnableUnarchivalConfirmation(false);
- if (Flags.enableNewArchivingIcon()) {
- params.setEnableIconOverlay(false);
- }
+ params.setEnableIconOverlay(!Flags.enableNewArchivingIcon());
launcherApps.setArchiveCompatibility(params);
}
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index 83c34ce..d57f8a0 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -254,8 +254,8 @@
PopupContainerWithArrow.dismissInvalidPopup(launcher)
}
- override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry?>?) {
- launcher.popupDataProvider.allWidgets = allWidgets
+ override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry>) {
+ launcher.widgetPickerDataProvider.setWidgets(allWidgets, /* defaultWidgets= */ listOf())
}
/** Returns the ids of the workspaces to bind. */
@@ -304,7 +304,8 @@
}
val widgetsListBaseEntry: WidgetsListBaseEntry =
- launcher.popupDataProvider.allWidgets.firstOrNull { item: WidgetsListBaseEntry ->
+ launcher.widgetPickerDataProvider.get().allWidgets.firstOrNull {
+ item: WidgetsListBaseEntry ->
item.mPkgItem.packageName == BuildConfig.APPLICATION_ID
} ?: return
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index a448228..fde7014 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -178,6 +178,11 @@
sIsRunningInTestHarness = true;
}
+ /** Disables running test in test harness mode */
+ public static void disableRunningInTestHarnessForTests() {
+ sIsRunningInTestHarness = false;
+ }
+
public static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
@@ -746,7 +751,8 @@
* | +--+ |
* | |
* +----------------+
- * This would be case delta % 4 == 2:
+ * This would be case delta % 4 == 2: // This is case was reverted to previous behaviour which
+ * doesn't match the illustration due to b/353965234
* +-------------+
* | |
* | |
@@ -768,7 +774,6 @@
int delta) {
int rdelta = ((delta % 4) + 4) % 4;
int origLeft = inOutBounds.left;
- int origTop = inOutBounds.top;
switch (rdelta) {
case 0:
return;
@@ -780,8 +785,6 @@
return;
case 2:
inOutBounds.left = parentWidth - inOutBounds.right;
- inOutBounds.top = parentHeight - inOutBounds.bottom;
- inOutBounds.bottom = parentHeight - origTop;
inOutBounds.right = parentWidth - origLeft;
return;
case 3:
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 56a7fef..ad660d2 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -71,6 +71,7 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
@@ -764,6 +765,16 @@
}
}
+ /**
+ * Force header height update with an offset. Used by {@link UniversalSearchInputView} to
+ * request {@link FloatingHeaderView} to update its maxTranslation for multiline search bar.
+ */
+ public void forceUpdateHeaderHeight(int offset) {
+ if (Flags.multilineSearchBar()) {
+ mHeader.updateSearchBarOffset(offset);
+ }
+ }
+
protected void updateHeaderScroll(int scrolledOffset) {
float prog1 = Utilities.boundToRange((float) scrolledOffset / mHeaderThreshold, 0f, 1f);
int headerColor = getHeaderColor(prog1);
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 89e6adc..a4f130a 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -262,11 +262,12 @@
writer.println(prefix + "\tAllAppsStore Apps[] size: " + mApps.length);
for (int i = 0; i < mApps.length; i++) {
writer.println(String.format(Locale.getDefault(),
- "%s\tPackage index, name, and class: " + "%d/%s:%s",
+ "%s\tPackage index, name, class, and description: %d/%s:%s, %s",
prefix,
i,
mApps[i].componentName.getPackageName(),
- mApps[i].componentName.getClassName()));
+ mApps[i].componentName.getClassName(),
+ mApps[i].contentDescription));
}
}
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 5d03a93..9271596 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
@@ -295,6 +296,14 @@
if (Flags.enablePrivateSpace()) {
position = addPrivateSpaceItems(position);
}
+ if (!mFastScrollerSections.isEmpty()) {
+ // After all the adapterItems are added, add a view to the bottom so that user can
+ // scroll all the way down.
+ mAdapterItems.add(new AdapterItem(VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO));
+ mFastScrollerSections.add(new FastScrollSectionInfo(
+ mFastScrollerSections.get(mFastScrollerSections.size() - 1).sectionName,
+ position++));
+ }
}
mAccessibilityResultsCount = (int) mAdapterItems.stream()
.filter(AdapterItem::isCountedForAccessibility).count();
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 4b38df8..60bf3ea 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -68,7 +68,8 @@
public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 5;
public static final int VIEW_TYPE_PRIVATE_SPACE_HEADER = 1 << 6;
public static final int VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER = 1 << 7;
- public static final int NEXT_ID = 8;
+ public static final int VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO = 1 << 8;
+ public static final int NEXT_ID = 9;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -247,6 +248,8 @@
case VIEW_TYPE_PRIVATE_SPACE_HEADER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.private_space_header, parent, false));
+ case VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO:
+ return new ViewHolder(new View(mActivityContext));
default:
if (mAdapterProvider.isViewSupported(viewType)) {
return mAdapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
@@ -324,6 +327,7 @@
== STATE_DISABLED ? null : new SectionDecorationInfo(mActivityContext,
ROUND_NOTHING, true /* decorateTogether */);
break;
+ case VIEW_TYPE_BOTTOM_VIEW_TO_SCROLL_TO:
case VIEW_TYPE_ALL_APPS_DIVIDER:
case VIEW_TYPE_WORK_DISABLED_CARD:
// nothing to do
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 92c589c..a2bd5dd 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -30,6 +30,7 @@
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder;
@@ -104,6 +105,8 @@
private boolean mFloatingRowsCollapsed;
// Total height of all current floating rows. Collapsed rows == 0 height.
private int mFloatingRowsHeight;
+ // Offset of search bar. Adds to the floating view height when multi-line is supported.
+ private int mSearchBarOffset = 0;
// This is initialized once during inflation and stays constant after that. Fixed views
// cannot be added or removed dynamically.
@@ -198,6 +201,14 @@
}
}
+ /**
+ * Offset floating rows height by search bar
+ */
+ void updateSearchBarOffset(int offset) {
+ mSearchBarOffset = offset;
+ onHeightUpdated();
+ }
+
@Override
public void onPluginDisconnected(AllAppsRow plugin) {
PluginHeaderRow row = mPluginRows.get(plugin);
@@ -258,9 +269,18 @@
mTabLayout.setVisibility(mTabsHidden ? GONE : visibility);
}
+ /** Returns whether search bar has multi-line support, and is currently in multi-line state. */
+ private boolean isSearchBarMultiline() {
+ return Flags.multilineSearchBar() && mSearchBarOffset > 0;
+ }
+
private void updateExpectedHeight() {
updateFloatingRowsHeight();
mMaxTranslation = 0;
+ boolean shouldAddSearchBarHeight = isSearchBarMultiline() && !Flags.floatingSearchBar();
+ if (shouldAddSearchBarHeight) {
+ mMaxTranslation += mSearchBarOffset;
+ }
if (mFloatingRowsCollapsed) {
return;
}
diff --git a/src/com/android/launcher3/allapps/FloatingMaskView.java b/src/com/android/launcher3/allapps/FloatingMaskView.java
index 606eb03..cee5e18 100644
--- a/src/com/android/launcher3/allapps/FloatingMaskView.java
+++ b/src/com/android/launcher3/allapps/FloatingMaskView.java
@@ -21,6 +21,7 @@
import android.view.ViewGroup;
import android.widget.ImageView;
+import androidx.annotation.VisibleForTesting;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.launcher3.R;
@@ -53,13 +54,21 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
- AllAppsRecyclerView allAppsContainerView =
- mActivityContext.getAppsView().getActiveRecyclerView();
+ setParameters((ViewGroup.MarginLayoutParams) getLayoutParams(),
+ mActivityContext.getAppsView().getActiveRecyclerView());
+ }
+
+ @VisibleForTesting
+ void setParameters(ViewGroup.MarginLayoutParams lp, AllAppsRecyclerView recyclerView) {
if (lp != null) {
- lp.rightMargin = allAppsContainerView.getPaddingRight();
- lp.leftMargin = allAppsContainerView.getPaddingLeft();
- mBottomBox.setMinimumHeight(allAppsContainerView.getPaddingBottom());
+ lp.rightMargin = recyclerView.getPaddingRight();
+ lp.leftMargin = recyclerView.getPaddingLeft();
+ getBottomBox().setMinimumHeight(recyclerView.getPaddingBottom());
}
}
+
+ @VisibleForTesting
+ ImageView getBottomBox() {
+ return mBottomBox;
+ }
}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index c1264d6..e215cab 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -403,6 +403,7 @@
mLockText.setHorizontallyScrolling(false);
mPrivateSpaceSettingsButton.setVisibility(
isPrivateSpaceSettingsAvailable() ? VISIBLE : GONE);
+ mPrivateSpaceSettingsButton.setClickable(isPrivateSpaceSettingsAvailable());
}
lockPill.setVisibility(VISIBLE);
lockPill.setOnClickListener(view -> lockingAction(/* lock */ true));
@@ -425,6 +426,7 @@
lockPill.setContentDescription(mLockedStateContentDesc);
mPrivateSpaceSettingsButton.setVisibility(GONE);
+ mPrivateSpaceSettingsButton.setClickable(false);
transitionView.setVisibility(GONE);
}
case STATE_TRANSITION -> {
@@ -660,10 +662,7 @@
return;
}
attachFloatingMaskView(expand);
- PropertySetter headerSetter = new AnimatedPropertySetter();
- headerSetter.add(updateSettingsGearAlpha(expand));
- headerSetter.add(updateLockTextAlpha(expand));
- AnimatorSet animatorSet = headerSetter.buildAnim();
+ AnimatorSet animatorSet = new AnimatedPropertySetter().buildAnim();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -708,12 +707,16 @@
}
}));
if (expand) {
- animatorSet.playTogether(animateAlphaOfIcons(true),
+ animatorSet.playTogether(updateSettingsGearAlpha(true),
+ updateLockTextAlpha(true),
+ animateAlphaOfIcons(true),
animatePillTransition(true),
translateFloatingMaskView(false));
} else {
AnimatorSet parallelSet = new AnimatorSet();
- parallelSet.playTogether(animateAlphaOfIcons(false),
+ parallelSet.playTogether(updateSettingsGearAlpha(false),
+ updateLockTextAlpha(false),
+ animateAlphaOfIcons(false),
animatePillTransition(false));
if (isPrivateSpaceHidden()) {
animatorSet.playSequentially(parallelSet,
@@ -794,6 +797,14 @@
@Override
public void onAnimationStart(Animator animator) {
mPrivateSpaceSettingsButton.setVisibility(VISIBLE);
+ mPrivateSpaceSettingsButton.setClickable(false);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (expand) {
+ mPrivateSpaceSettingsButton.setClickable(true);
+ }
}
});
return settingsAlphaAnim;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index ad32fc2..f54fc57 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -213,9 +213,13 @@
Collections.sort(hotseatToBeAdded);
Collections.sort(workspaceToBeAdded);
+ List<Integer> idsInUse = dstWorkspaceItems.stream().map(entry -> entry.id).collect(
+ Collectors.toList());
+ idsInUse.addAll(dstHotseatItems.stream().map(entry -> entry.id).toList());
+
// Migrate hotseat
solveHotseatPlacement(helper, destHotseatSize,
- srcReader, destReader, dstHotseatItems, hotseatToBeAdded);
+ srcReader, destReader, dstHotseatItems, hotseatToBeAdded, idsInUse);
// Migrate workspace.
// First we create a collection of the screens
@@ -230,7 +234,7 @@
Log.d(TAG, "Migrating " + screenId);
}
solveGridPlacement(helper, srcReader,
- destReader, screenId, trgX, trgY, workspaceToBeAdded);
+ destReader, screenId, trgX, trgY, workspaceToBeAdded, idsInUse);
if (workspaceToBeAdded.isEmpty()) {
break;
}
@@ -241,7 +245,7 @@
int screenId = destReader.mLastScreenId + 1;
while (!workspaceToBeAdded.isEmpty()) {
solveGridPlacement(helper, srcReader, destReader, screenId, trgX, trgY,
- workspaceToBeAdded);
+ workspaceToBeAdded, srcWorkspaceItems.stream().map(entry -> entry.id).toList());
screenId++;
}
@@ -257,47 +261,57 @@
private static void calcDiff(@NonNull final List<DbEntry> src,
@NonNull final List<DbEntry> dest, @NonNull final List<DbEntry> toBeAdded,
@NonNull final IntArray toBeRemoved) {
+ HashMap<DbEntry, Integer> entryCountDiff = new HashMap<>();
+ src.forEach(entry ->
+ entryCountDiff.put(entry, entryCountDiff.getOrDefault(entry, 0) + 1));
+ dest.forEach(entry ->
+ entryCountDiff.put(entry, entryCountDiff.getOrDefault(entry, 0) - 1));
+
src.forEach(entry -> {
- if (!dest.contains(entry)) {
+ if (entryCountDiff.get(entry) > 0) {
toBeAdded.add(entry);
+ entryCountDiff.put(entry, entryCountDiff.get(entry) - 1);
}
});
+
dest.forEach(entry -> {
- if (!src.contains(entry)) {
+ if (entryCountDiff.get(entry) < 0) {
toBeRemoved.add(entry.id);
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
entry.mFolderItems.values().forEach(ids -> ids.forEach(toBeRemoved::add));
}
+ entryCountDiff.put(entry, entryCountDiff.get(entry) + 1);
}
});
}
private static void insertEntryInDb(DatabaseHelper helper, DbEntry entry,
- String srcTableName, String destTableName) {
- int id = copyEntryAndUpdate(helper, entry, srcTableName, destTableName);
-
+ String srcTableName, String destTableName, List<Integer> idsInUse) {
+ int id = copyEntryAndUpdate(helper, entry, srcTableName, destTableName, idsInUse);
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER
|| entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR) {
for (Set<Integer> itemIds : entry.mFolderItems.values()) {
for (int itemId : itemIds) {
- copyEntryAndUpdate(helper, itemId, id, srcTableName, destTableName);
+ copyEntryAndUpdate(helper, itemId, id, srcTableName, destTableName, idsInUse);
}
}
}
}
private static int copyEntryAndUpdate(DatabaseHelper helper,
- DbEntry entry, String srcTableName, String destTableName) {
- return copyEntryAndUpdate(helper, entry, -1, -1, srcTableName, destTableName);
+ DbEntry entry, String srcTableName, String destTableName, List<Integer> idsInUse) {
+ return copyEntryAndUpdate(
+ helper, entry, -1, -1, srcTableName, destTableName, idsInUse);
}
- private static int copyEntryAndUpdate(DatabaseHelper helper,
- int id, int folderId, String srcTableName, String destTableName) {
- return copyEntryAndUpdate(helper, null, id, folderId, srcTableName, destTableName);
+ private static int copyEntryAndUpdate(DatabaseHelper helper, int id,
+ int folderId, String srcTableName, String destTableName, List<Integer> idsInUse) {
+ return copyEntryAndUpdate(
+ helper, null, id, folderId, srcTableName, destTableName, idsInUse);
}
- private static int copyEntryAndUpdate(DatabaseHelper helper, DbEntry entry,
- int id, int folderId, String srcTableName, String destTableName) {
+ private static int copyEntryAndUpdate(DatabaseHelper helper, DbEntry entry, int id,
+ int folderId, String srcTableName, String destTableName, List<Integer> idsInUse) {
int newId = -1;
Cursor c = helper.getWritableDatabase().query(srcTableName, null,
LauncherSettings.Favorites._ID + " = '" + (entry != null ? entry.id : id) + "'",
@@ -311,6 +325,9 @@
values.put(LauncherSettings.Favorites.CONTAINER, folderId);
}
newId = helper.generateNewItemId();
+ while (idsInUse.contains(newId)) {
+ newId = helper.generateNewItemId();
+ }
values.put(LauncherSettings.Favorites._ID, newId);
helper.getWritableDatabase().insert(destTableName, null, values);
}
@@ -343,7 +360,7 @@
private static void solveGridPlacement(@NonNull final DatabaseHelper helper,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
final int screenId, final int trgX, final int trgY,
- @NonNull final List<DbEntry> sortedItemsToPlace) {
+ @NonNull final List<DbEntry> sortedItemsToPlace, List<Integer> idsInUse) {
final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
final Point trg = new Point(trgX, trgY);
final Point next = new Point(0, screenId == 0
@@ -366,7 +383,8 @@
continue;
}
if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
- insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
+ insertEntryInDb(
+ helper, entry, srcReader.mTableName, destReader.mTableName, idsInUse);
iterator.remove();
}
}
@@ -407,7 +425,7 @@
@NonNull final DatabaseHelper helper, final int hotseatSize,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
@NonNull final List<DbEntry> placedHotseatItems,
- @NonNull final List<DbEntry> itemsToPlace) {
+ @NonNull final List<DbEntry> itemsToPlace, List<Integer> idsInUse) {
final boolean[] occupied = new boolean[hotseatSize];
for (DbEntry entry : placedHotseatItems) {
@@ -422,7 +440,8 @@
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
- insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
+ insertEntryInDb(
+ helper, entry, srcReader.mTableName, destReader.mTableName, idsInUse);
occupied[entry.screenId] = true;
}
}
@@ -564,12 +583,12 @@
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
entry.mProvider = c.getString(indexAppWidgetProvider);
+ entry.appWidgetId = c.getInt(indexAppWidgetId);
ComponentName cn = ComponentName.unflattenFromString(entry.mProvider);
verifyPackage(cn.getPackageName());
- int widgetId = c.getInt(indexAppWidgetId);
LauncherAppWidgetProviderInfo pInfo = widgetManagerHelper
- .getLauncherAppWidgetInfo(widgetId, cn);
+ .getLauncherAppWidgetInfo(entry.appWidgetId, cn);
Point spans = null;
if (pInfo != null) {
spans = pInfo.getMinSpans();
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 7e1d40d..da1a221 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -83,6 +83,7 @@
import org.xmlpull.v1.XmlPullParser;
+import java.io.File;
import java.io.InputStream;
import java.io.StringReader;
@@ -104,10 +105,30 @@
mContext = context;
}
+ private void printDBs(String prefix) {
+ try {
+ File directory = new File(
+ mContext.getDatabasePath(InvariantDeviceProfile.INSTANCE.get(mContext).dbFile)
+ .getParent()
+ );
+ if (directory.exists()) {
+ for (File file : directory.listFiles()) {
+ Log.d("b/353505773", prefix + "Database file: " + file.getName());
+ }
+ } else {
+ Log.d("b/353505773", prefix + "No files found in the database directory");
+ }
+ } catch (Exception e) {
+ Log.e("b/353505773", prefix + e.getMessage());
+ }
+ }
+
private synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
mOpenHelper = createDatabaseHelper(false /* forMigration */);
+ printDBs("before: ");
RestoreDbTask.restoreIfNeeded(mContext, this);
+ printDBs("after: ");
}
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 35064cf..c949ce6 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -89,7 +89,7 @@
if (!WIDGETS_ENABLED) {
return Collections.emptyMap();
}
- return mWidgetsByPackageItem;
+ return new HashMap<>(mWidgetsByPackageItem);
}
/**
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index e44ea1d..a691e45 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -43,6 +43,7 @@
import android.view.animation.OvershootInterpolator;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
@@ -131,7 +132,8 @@
private float mCurrentPosition;
private float mFinalPosition;
private boolean mIsScrollPaused;
- private boolean mIsTwoPanels;
+ @VisibleForTesting
+ boolean mIsTwoPanels;
private ObjectAnimator mAnimator;
private @Nullable ObjectAnimator mAlphaAnimator;
@@ -477,6 +479,21 @@
return sTempRect;
}
+ @VisibleForTesting
+ int getActivePage() {
+ return mActivePage;
+ }
+
+ @VisibleForTesting
+ int getNumPages() {
+ return mNumPages;
+ }
+
+ @VisibleForTesting
+ float getCurrentPosition() {
+ return mCurrentPosition;
+ }
+
private class MyOutlineProver extends ViewOutlineProvider {
@Override
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index 24d58f3..c117be4 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -32,7 +32,6 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 7339111..e861961 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -183,6 +183,11 @@
mUserToSerialMap.put(userHandle, info);
}
+ @VisibleForTesting
+ public void putToPreInstallCache(UserHandle userHandle, List<String> preInstalledApps) {
+ mUserToPreInstallAppMap.put(userHandle, preInstalledApps);
+ }
+
/**
* @see UserManager#getUserProfiles()
*/
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 7e139c3..8a5e388 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -24,28 +24,20 @@
import androidx.annotation.Nullable;
import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
-import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-import com.android.launcher3.widget.model.WidgetsListContentEntry;
-import com.android.launcher3.widget.picker.WidgetRecommendationCategory;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.function.Consumer;
-import java.util.function.Function;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
/**
* Provides data for the popup menu that appears after long-clicking on apps.
@@ -62,18 +54,6 @@
/** Maps packages to their DotInfo's . */
private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
- /** All installed widgets. */
- private List<WidgetsListBaseEntry> mAllWidgets = List.of();
- /**
- * Selectively chosen installed widgets which may be preferred for default display over the list
- * of all widgets.
- */
- private List<WidgetsListBaseEntry> mDefaultWidgets = List.of();
- /** Widgets that can be recommended to the users. */
- private List<ItemInfo> mRecommendedWidgets = List.of();
-
- private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
-
public PopupDataProvider(Consumer<Predicate<PackageUserKey>> notificationDotsChangeListener) {
mNotificationDotsChangeListener = notificationDotsChangeListener;
}
@@ -188,124 +168,8 @@
})) ? dotInfo : null;
}
- /**
- * Sets a list of recommended widgets ordered by their order of appearance in the widgets
- * recommendation UI.
- */
- public void setRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
- mRecommendedWidgets = recommendedWidgets;
- mChangeListener.onRecommendedWidgetsBound();
- }
-
- public void setAllWidgets(List<WidgetsListBaseEntry> allWidgets) {
- mAllWidgets = allWidgets;
- mDefaultWidgets = List.of();
- mChangeListener.onWidgetsBound();
- }
-
- /**
- * Sets the list of widgets to be displayed by default and a complete list that can be displayed
- * when user chooses to show all widgets.
- */
- public void setAllWidgets(List<WidgetsListBaseEntry> allWidgets,
- List<WidgetsListBaseEntry> defaultWidgets) {
- mAllWidgets = allWidgets;
- mDefaultWidgets = defaultWidgets;
- mChangeListener.onWidgetsBound();
- }
-
- public void setChangeListener(PopupDataChangeListener listener) {
- mChangeListener = listener == null ? PopupDataChangeListener.INSTANCE : listener;
- }
-
- public List<WidgetsListBaseEntry> getAllWidgets() {
- return mAllWidgets;
- }
-
- /**
- * Returns a "selectively" chosen list of widgets that may be preferred to be shown by default
- * instead of a complete list.
- */
- public List<WidgetsListBaseEntry> getDefaultWidgets() {
- return mDefaultWidgets;
- }
-
- /** Returns a list of recommended widgets. */
- public List<WidgetItem> getRecommendedWidgets() {
- HashMap<ComponentKey, WidgetItem> allWidgetItems = new HashMap<>();
- mAllWidgets.stream()
- .filter(entry -> entry instanceof WidgetsListContentEntry)
- .forEach(entry -> ((WidgetsListContentEntry) entry).mWidgets
- .forEach(widget -> allWidgetItems.put(
- new ComponentKey(widget.componentName, widget.user), widget)));
- return mRecommendedWidgets.stream()
- .map(recommendedWidget -> allWidgetItems.get(
- new ComponentKey(recommendedWidget.getTargetComponent(),
- recommendedWidget.user)))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- }
-
- /** Returns the recommended widgets mapped by their category. */
- @NonNull
- public Map<WidgetRecommendationCategory, List<WidgetItem>> getCategorizedRecommendedWidgets() {
- Map<ComponentKey, WidgetItem> allWidgetItems = mAllWidgets.stream()
- .filter(entry -> entry instanceof WidgetsListContentEntry)
- .flatMap(entry -> entry.mWidgets.stream())
- .distinct()
- .collect(Collectors.toMap(
- widget -> new ComponentKey(widget.componentName, widget.user),
- Function.identity()
- ));
- return mRecommendedWidgets.stream()
- .filter(itemInfo -> itemInfo instanceof PendingAddWidgetInfo
- && ((PendingAddWidgetInfo) itemInfo).recommendationCategory != null)
- .collect(Collectors.groupingBy(
- it -> ((PendingAddWidgetInfo) it).recommendationCategory,
- Collectors.collectingAndThen(
- Collectors.toList(),
- list -> list.stream()
- .map(it -> allWidgetItems.get(
- new ComponentKey(it.getTargetComponent(),
- it.user)))
- .filter(Objects::nonNull)
- .collect(Collectors.toList())
- )
- ));
- }
-
- public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
- return mAllWidgets.stream()
- .filter(row -> row instanceof WidgetsListContentEntry
- && row.mPkgItem.packageName.equals(packageUserKey.mPackageName))
- .flatMap(row -> ((WidgetsListContentEntry) row).mWidgets.stream())
- .filter(widget -> packageUserKey.mUser.equals(widget.user))
- .collect(Collectors.toList());
- }
-
- /** Gets the WidgetsListContentEntry for the currently selected header. */
- public WidgetsListContentEntry getSelectedAppWidgets(PackageUserKey packageUserKey,
- boolean useDefault) {
- List<WidgetsListBaseEntry> widgets = useDefault ? mDefaultWidgets : mAllWidgets;
- return (WidgetsListContentEntry) widgets.stream()
- .filter(row -> row instanceof WidgetsListContentEntry
- && PackageUserKey.fromPackageItemInfo(row.mPkgItem).equals(packageUserKey))
- .findAny()
- .orElse(null);
- }
-
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "PopupDataProvider:");
writer.println(prefix + "\tmPackageUserToDotInfos:" + mPackageUserToDotInfos);
}
-
- public interface PopupDataChangeListener {
-
- PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
-
- default void onWidgetsBound() { }
-
- /** A callback to get notified when recommended widgets are bound. */
- default void onRecommendedWidgetsBound() { }
- }
}
diff --git a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
index 4c94f94..1fd3557 100644
--- a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
@@ -19,6 +19,8 @@
import android.view.View;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider.WidgetPickerDataChangeListener;
/**
* Utility class to handle updates while the popup is visible (like widgets and
@@ -27,7 +29,7 @@
* @param <T> The activity on which the popup shows
*/
public abstract class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
- PopupDataProvider.PopupDataChangeListener, View.OnAttachStateChangeListener {
+ WidgetPickerDataChangeListener, View.OnAttachStateChangeListener {
protected final T mContext;
protected final PopupContainerWithArrow<T> mPopupContainerWithArrow;
@@ -40,19 +42,25 @@
@Override
public void onViewAttachedToWindow(View view) {
- PopupDataProvider popupDataProvider = mContext.getPopupDataProvider();
+ WidgetPickerDataProvider widgetsDataProvider = mContext.getWidgetPickerDataProvider();
- if (popupDataProvider != null) {
- popupDataProvider.setChangeListener(this);
+ if (widgetsDataProvider != null) {
+ widgetsDataProvider.setChangeListener(this);
}
}
@Override
public void onViewDetachedFromWindow(View view) {
- PopupDataProvider popupDataProvider = mContext.getPopupDataProvider();
+ WidgetPickerDataProvider widgetsDataProvider = mContext.getWidgetPickerDataProvider();
- if (popupDataProvider != null) {
- popupDataProvider.setChangeListener(null);
+ if (widgetsDataProvider != null) {
+ widgetsDataProvider.setChangeListener(null);
}
}
+
+ @Override
+ public void onWidgetsBound() {} // NO_OP
+
+ @Override
+ public void onRecommendedWidgetsBound() {} // NO_OP
}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 83e9810..f7e1168 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -5,6 +5,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
+import static com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.findAllWidgetsForPackageUser;
import android.app.ActivityOptions;
import android.content.ComponentName;
@@ -28,7 +29,6 @@
import com.android.launcher3.SecondaryDropTarget;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.PrivateProfileManager;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
@@ -39,9 +39,9 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.WidgetsBottomSheet;
+import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
import java.util.Arrays;
-import java.util.List;
/**
* Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
@@ -107,11 +107,12 @@
}
public static final Factory<ActivityContext> WIDGETS = (context, itemInfo, originalView) -> {
- if (itemInfo.getTargetComponent() == null) return null;
- final List<WidgetItem> widgets =
- context.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
- itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
- if (widgets.isEmpty()) {
+ final PackageUserKey packageUserKey = PackageUserKey.fromItemInfo(itemInfo);
+ if (packageUserKey == null) return null;
+
+ final WidgetPickerData data = context.getWidgetPickerDataProvider().get();
+ if (findAllWidgetsForPackageUser(data, packageUserKey).isEmpty()) {
+ // hides widget picker shortcut if there are no widgets for the package.
return null;
}
return new Widgets(context, itemInfo, originalView);
diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
index f895b30..0a7beab 100644
--- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
+++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
@@ -17,6 +17,7 @@
package com.android.launcher3.recyclerview
import android.content.Context
+import android.util.Log
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.Companion.PROTECTED
@@ -24,6 +25,7 @@
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.android.launcher3.BubbleTextView
+import com.android.launcher3.BuildConfig
import com.android.launcher3.allapps.BaseAllAppsAdapter
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.CancellableTask
@@ -32,6 +34,7 @@
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.ActivityContext.ActivityContextDelegate
+import java.lang.IllegalStateException
const val PREINFLATE_ICONS_ROW_COUNT = 4
const val EXTRA_ICONS_COUNT = 2
@@ -47,6 +50,12 @@
@VisibleForTesting(otherwise = PROTECTED)
var mCancellableTask: CancellableTask<List<ViewHolder>>? = null
+ companion object {
+ private const val TAG = "AllAppsRecyclerViewPool"
+ private const val NULL_LAYOUT_MANAGER_ERROR_STRING =
+ "activeRv's layoutManager should not be null"
+ }
+
/**
* Preinflate app icons. If all apps RV cannot be scrolled down, we don't need to preinflate.
*/
@@ -54,6 +63,15 @@
val appsView = context.appsView ?: return
val activeRv: RecyclerView = appsView.activeRecyclerView ?: return
+ if (activeRv.layoutManager == null) {
+ if (BuildConfig.IS_STUDIO_BUILD) {
+ throw IllegalStateException(NULL_LAYOUT_MANAGER_ERROR_STRING)
+ } else {
+ Log.e(TAG, NULL_LAYOUT_MANAGER_ERROR_STRING)
+ }
+ return
+ }
+
// Create a separate context dedicated for all apps preinflation thread. The goal is to
// create a separate AssetManager obj internally to avoid lock contention with
// AssetManager obj that is associated with the launcher context on the main thread.
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 0299a23..9b3292d 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -59,6 +59,7 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.HashMap;
import java.util.Map;
@@ -76,6 +77,7 @@
private View mAppsButton;
private PopupDataProvider mPopupDataProvider;
+ private WidgetPickerDataProvider mWidgetPickerDataProvider;
private boolean mAppDrawerShown = false;
@@ -315,6 +317,11 @@
}
@Override
+ public WidgetPickerDataProvider getWidgetPickerDataProvider() {
+ return mWidgetPickerDataProvider;
+ }
+
+ @Override
public OnClickListener getItemOnClickListener() {
return this::onIconClicked;
}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 52ce4e8..bd9298b 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -44,11 +44,13 @@
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceGroup.PreferencePositionCallback;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BuildConfig;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.R;
import com.android.launcher3.states.RotationHelper;
@@ -165,6 +167,7 @@
private boolean mRestartOnResume = false;
private String mHighLightKey;
+
private boolean mPreferenceHighlighted = false;
@Override
@@ -198,11 +201,62 @@
}
}
+ // If the target preference is not in the current preference screen, find the parent
+ // preference screen that contains the target preference and set it as the preference
+ // screen.
+ if (Flags.navigateToChildPreference()
+ && mHighLightKey != null
+ && !isKeyInPreferenceGroup(mHighLightKey, screen)) {
+ final PreferenceScreen parentPreferenceScreen =
+ findParentPreference(screen, mHighLightKey);
+ if (parentPreferenceScreen != null && getActivity() != null) {
+ if (!TextUtils.isEmpty(parentPreferenceScreen.getTitle())) {
+ getActivity().setTitle(parentPreferenceScreen.getTitle());
+ }
+ setPreferenceScreen(parentPreferenceScreen);
+ return;
+ }
+ }
+
if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
getActivity().setTitle(getPreferenceScreen().getTitle());
}
}
+ private boolean isKeyInPreferenceGroup(String targetKey, PreferenceGroup parent) {
+ for (int i = 0; i < parent.getPreferenceCount(); i++) {
+ Preference pref = parent.getPreference(i);
+ if (pref.getKey() != null && pref.getKey().equals(targetKey)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Finds the parent preference screen for the given target key.
+ *
+ * @param parent the parent preference screen
+ * @param targetKey the key of the preference to find
+ * @return the parent preference screen that contains the target preference
+ */
+ @Nullable
+ private PreferenceScreen findParentPreference(PreferenceScreen parent, String targetKey) {
+ for (int i = 0; i < parent.getPreferenceCount(); i++) {
+ Preference pref = parent.getPreference(i);
+ if (pref instanceof PreferenceScreen) {
+ PreferenceScreen foundKey = findParentPreference((PreferenceScreen) pref,
+ targetKey);
+ if (foundKey != null) {
+ return foundKey;
+ }
+ } else if (pref.getKey() != null && pref.getKey().equals(targetKey)) {
+ return parent;
+ }
+ }
+ return null;
+ }
+
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index 3d4b409..f183f18 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -76,4 +76,5 @@
* When turned on, we enable zero state web data loader related logging.
*/
public static final String ZERO_WEB_DATA_LOADER = "ZeroStateWebDataLoaderLog";
+ public static final String SEARCH_TARGET_UTIL_LOG = "SearchTargetUtilLog";
}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.kt b/src/com/android/launcher3/util/OnboardingPrefs.kt
index ac6e97c..771594e 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.kt
+++ b/src/com/android/launcher3/util/OnboardingPrefs.kt
@@ -16,6 +16,7 @@
package com.android.launcher3.util
import android.content.Context
+import androidx.annotation.VisibleForTesting
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.backedUpItem
@@ -26,7 +27,7 @@
val sharedPrefKey: String,
val maxCount: Int,
) {
- private val prefItem = backedUpItem(sharedPrefKey, 0)
+ @VisibleForTesting val prefItem = backedUpItem(sharedPrefKey, 0)
/** @return The number of times we have seen the given event. */
fun get(c: Context): Int {
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 95624b1..837d7bc 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -186,12 +186,6 @@
public int stagePosition = STAGE_POSITION_UNDEFINED;
@StageType
public int stageType = STAGE_TYPE_UNDEFINED;
-
- @Override
- public String toString() {
- return "SplitStageInfo { taskId=" + taskId
- + ", stagePosition=" + stagePosition + ", stageType=" + stageType + " }";
- }
}
public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index cfac91a..d3160e0 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -81,6 +81,7 @@
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.List;
@@ -266,6 +267,14 @@
return null;
}
+ /**
+ * Returns the {@link WidgetPickerDataProvider} that can be used to read widgets for display.
+ */
+ @Nullable
+ default WidgetPickerDataProvider getWidgetPickerDataProvider() {
+ return null;
+ }
+
@Nullable
default StringCache getStringCache() {
return null;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index c59e295..1c0d94c 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -45,13 +45,13 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.views.AbstractSlideInView;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider.WidgetPickerDataChangeListener;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -60,7 +60,7 @@
*/
public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
implements OnClickListener, OnLongClickListener,
- PopupDataProvider.PopupDataChangeListener, Insettable, OnDeviceProfileChangeListener {
+ WidgetPickerDataChangeListener, Insettable, OnDeviceProfileChangeListener {
/** The default number of cells that can fit horizontally in a widget sheet. */
public static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
@@ -106,14 +106,14 @@
WindowInsets windowInsets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), getRootWindowInsets(), new Rect());
mNavBarScrimHeight = getNavBarScrimHeight(windowInsets);
- mActivityContext.getPopupDataProvider().setChangeListener(this);
+ mActivityContext.getWidgetPickerDataProvider().setChangeListener(this);
mActivityContext.addOnDeviceProfileChangeListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mActivityContext.getPopupDataProvider().setChangeListener(null);
+ mActivityContext.getWidgetPickerDataProvider().setChangeListener(null);
mActivityContext.removeOnDeviceProfileChangeListener(this);
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 71d8503..91b899c 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.util.Executors;
@@ -77,6 +78,11 @@
mViewToRecycle = viewToRecycle;
}
+ @VisibleForTesting
+ @Nullable ListenableHostView getViewToRecycle() {
+ return mViewToRecycle;
+ }
+
@Override
@NonNull
public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 894099d..ddbd291 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -17,6 +17,7 @@
package com.android.launcher3.widget;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
+import static com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.findAllWidgetsForPackageUser;
import android.content.Context;
import android.graphics.Rect;
@@ -40,6 +41,7 @@
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
import com.android.launcher3.widget.util.WidgetsTableUtils;
import java.util.List;
@@ -124,10 +126,10 @@
@Override
public void onWidgetsBound() {
- List<WidgetItem> widgets = mActivityContext.getPopupDataProvider().getWidgetsForPackageUser(
- new PackageUserKey(
- mOriginalItemInfo.getTargetComponent().getPackageName(),
- mOriginalItemInfo.user));
+ final WidgetPickerData data = mActivityContext.getWidgetPickerDataProvider().get();
+ final PackageUserKey packageUserKey = PackageUserKey.fromItemInfo(mOriginalItemInfo);
+ List<WidgetItem> widgets = packageUserKey != null ? findAllWidgetsForPackageUser(data,
+ packageUserKey) : List.of();
TableLayout widgetsTable = findViewById(R.id.widgets_table);
widgetsTable.removeAllViews();
@@ -247,4 +249,7 @@
}
}
}
+
+ @Override
+ public void onRecommendedWidgetsBound() {} // no op
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategory.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategory.java
index 072d1d5..a68effd 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategory.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategory.java
@@ -19,6 +19,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import com.android.launcher3.R;
+
import java.util.Objects;
/**
@@ -26,6 +28,10 @@
* option in the pop-up opened on long press of launcher workspace).
*/
public class WidgetRecommendationCategory implements Comparable<WidgetRecommendationCategory> {
+ public static WidgetRecommendationCategory DEFAULT_WIDGET_RECOMMENDATION_CATEGORY =
+ new WidgetRecommendationCategory(
+ R.string.others_widget_recommendation_category_label, /*order=*/0);
+
/** Resource id that holds the user friendly label for the category. */
@StringRes
public final int categoryTitleRes;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 21b7be4..2af8e6f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -74,6 +74,7 @@
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -471,7 +472,7 @@
* Returns all displayable widgets.
*/
protected List<WidgetsListBaseEntry> getWidgetsToDisplay() {
- return mActivityContext.getPopupDataProvider().getAllWidgets();
+ return mActivityContext.getWidgetPickerDataProvider().get().getAllWidgets();
}
@Override
@@ -572,12 +573,11 @@
if (mIsInSearchMode) {
return;
}
-
if (enableCategorizedWidgetSuggestions()) {
// We avoid applying new recommendations when some are already displayed.
if (mRecommendedWidgetsMap.isEmpty()) {
mRecommendedWidgetsMap =
- mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets();
+ mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
}
mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
mRecommendedWidgetsMap,
@@ -589,17 +589,20 @@
);
} else {
if (mRecommendedWidgets.isEmpty()) {
- mRecommendedWidgets =
- mActivityContext.getPopupDataProvider().getRecommendedWidgets();
+ mRecommendedWidgets = mActivityContext.getWidgetPickerDataProvider().get()
+ .getRecommendations()
+ .values().stream()
+ .flatMap(Collection::stream).toList();
+ mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
+ mRecommendedWidgets,
+ mDeviceProfile,
+ /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
+ /* availableWidth= */ mMaxSpanPerRow,
+ /* cellPadding= */ mWidgetCellHorizontalPadding
+ );
}
- mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
- mRecommendedWidgets,
- mDeviceProfile,
- /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
- /* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding
- );
}
+
mWidgetRecommendationsContainer.setVisibility(
mRecommendedWidgetsCount > 0 ? VISIBLE : GONE);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index c4c755a..c2cd903 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
import static com.android.launcher3.widget.picker.WidgetsListItemAnimator.WIDGET_LIST_ITEM_APPEARANCE_DELAY;
+import static com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.findContentEntryForPackageUser;
import android.content.Context;
import android.graphics.Rect;
@@ -287,9 +288,9 @@
@Override
protected List<WidgetsListBaseEntry> getWidgetsToDisplay() {
List<WidgetsListBaseEntry> allWidgets =
- mActivityContext.getPopupDataProvider().getAllWidgets();
+ mActivityContext.getWidgetPickerDataProvider().get().getAllWidgets();
List<WidgetsListBaseEntry> defaultWidgets =
- mActivityContext.getPopupDataProvider().getDefaultWidgets();
+ mActivityContext.getWidgetPickerDataProvider().get().getDefaultWidgets();
if (allWidgets.isEmpty() || defaultWidgets.isEmpty()) {
// no menu if there are no default widgets to show
@@ -359,7 +360,7 @@
WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
packageItemInfo,
/*titleSectionName=*/ suggestionsHeaderTitle,
- /*items=*/ mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
+ /*items=*/ List.of(), // not necessary
/*visibleWidgetsCount=*/ 0)
.withWidgetListShown();
@@ -509,11 +510,11 @@
final boolean isUserClick = mSelectedHeader != null
&& !getAccessibilityInitialFocusView().isAccessibilityFocused();
mSelectedHeader = selectedHeader;
- WidgetsListContentEntry contentEntry =
- mActivityContext.getPopupDataProvider().getSelectedAppWidgets(
- selectedHeader, /*useDefault=*/
- (mWidgetOptionsMenuState != null
- && !mWidgetOptionsMenuState.showAllWidgets));
+ final boolean showDefaultWidgets = mWidgetOptionsMenuState != null
+ && !mWidgetOptionsMenuState.showAllWidgets;
+ WidgetsListContentEntry contentEntry = findContentEntryForPackageUser(
+ mActivityContext.getWidgetPickerDataProvider().get(),
+ selectedHeader, showDefaultWidgets);
if (contentEntry == null || mRightPane == null) {
return;
diff --git a/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt b/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
new file mode 100644
index 0000000..46d3e7a
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProvider.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.widget.picker.model
+
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.widget.model.WidgetsListBaseEntry
+import com.android.launcher3.widget.picker.model.data.WidgetPickerData
+import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withRecommendedWidgets
+import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withWidgets
+import java.io.PrintWriter
+
+/**
+ * Provides [WidgetPickerData] to various views such as widget picker, app-specific widget picker,
+ * widgets shortcut.
+ */
+class WidgetPickerDataProvider {
+ /** All the widgets data provided for the views */
+ private var mWidgetPickerData: WidgetPickerData = WidgetPickerData()
+
+ private var changeListener: WidgetPickerDataChangeListener? = null
+
+ /** Sets a listener to be called back when widget data is updated. */
+ fun setChangeListener(changeListener: WidgetPickerDataChangeListener?) {
+ this.changeListener = changeListener
+ }
+
+ /** Returns the current snapshot of [WidgetPickerData]. */
+ fun get(): WidgetPickerData {
+ return mWidgetPickerData
+ }
+
+ /**
+ * Updates the widgets available to the widget picker.
+ *
+ * Generally called when the widgets model has new data.
+ */
+ @JvmOverloads
+ fun setWidgets(
+ allWidgets: List<WidgetsListBaseEntry>,
+ defaultWidgets: List<WidgetsListBaseEntry> = listOf()
+ ) {
+ mWidgetPickerData =
+ mWidgetPickerData.withWidgets(allWidgets = allWidgets, defaultWidgets = defaultWidgets)
+ changeListener?.onWidgetsBound()
+ }
+
+ /**
+ * Makes the widget recommendations available to the widget picker
+ *
+ * Generally called when new widget predictions are available.
+ */
+ fun setWidgetRecommendations(recommendations: List<ItemInfo>) {
+ mWidgetPickerData = mWidgetPickerData.withRecommendedWidgets(recommendations)
+ changeListener?.onRecommendedWidgetsBound()
+ }
+
+ /** Writes the current state to the provided writer. */
+ fun dump(prefix: String, writer: PrintWriter) {
+ writer.println(prefix + "WidgetPickerDataProvider:")
+ writer.println("$prefix\twidgetPickerData:$mWidgetPickerData")
+ }
+
+ interface WidgetPickerDataChangeListener {
+ /** A callback to get notified when widgets are bound. */
+ fun onWidgetsBound()
+
+ /** A callback to get notified when recommended widgets are bound. */
+ fun onRecommendedWidgetsBound()
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/model/data/WidgetPickerData.kt b/src/com/android/launcher3/widget/picker/model/data/WidgetPickerData.kt
new file mode 100644
index 0000000..3332ef0
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/model/data/WidgetPickerData.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.widget.picker.model.data
+
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.PackageUserKey
+import com.android.launcher3.widget.PendingAddWidgetInfo
+import com.android.launcher3.widget.model.WidgetsListBaseEntry
+import com.android.launcher3.widget.model.WidgetsListContentEntry
+import com.android.launcher3.widget.picker.WidgetRecommendationCategory
+import com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY
+
+// This file contains WidgetPickerData and utility functions to operate on it.
+
+/** Widget data for display in the widget picker. */
+data class WidgetPickerData(
+ val allWidgets: List<WidgetsListBaseEntry> = listOf(),
+ val defaultWidgets: List<WidgetsListBaseEntry> = listOf(),
+ val recommendations: Map<WidgetRecommendationCategory, List<WidgetItem>> = mapOf(),
+)
+
+/** Provides utility methods to work with a [WidgetPickerData] object. */
+object WidgetPickerDataUtils {
+ /**
+ * Returns a [WidgetPickerData] with the provided widgets.
+ *
+ * When [defaultWidgets] is not passed, defaults from previous object are not copied over.
+ * Defaults (if supported) should be updated when all widgets are updated.
+ */
+ fun WidgetPickerData.withWidgets(
+ allWidgets: List<WidgetsListBaseEntry>,
+ defaultWidgets: List<WidgetsListBaseEntry> = listOf()
+ ): WidgetPickerData {
+ return copy(allWidgets = allWidgets, defaultWidgets = defaultWidgets)
+ }
+
+ /** Returns a [WidgetPickerData] with the given recommendations set. */
+ fun WidgetPickerData.withRecommendedWidgets(recommendations: List<ItemInfo>): WidgetPickerData {
+ val allWidgetsMap: Map<ComponentKey, WidgetItem> =
+ allWidgets
+ .filterIsInstance<WidgetsListContentEntry>()
+ .flatMap { it.mWidgets }
+ .filterNotNull()
+ .distinct()
+ .associateBy { it } // as ComponentKey
+
+ val categoriesMap =
+ recommendations
+ .filterIsInstance<PendingAddWidgetInfo>()
+ .filter { allWidgetsMap.containsKey(ComponentKey(it.targetComponent, it.user)) }
+ .groupBy { it.recommendationCategory ?: DEFAULT_WIDGET_RECOMMENDATION_CATEGORY }
+ .mapValues { (_, pendingAddWidgetInfos) ->
+ pendingAddWidgetInfos.map {
+ allWidgetsMap[ComponentKey(it.targetComponent, it.user)] as WidgetItem
+ }
+ }
+
+ return copy(recommendations = categoriesMap)
+ }
+
+ /** Finds all [WidgetItem]s available for the provided package user. */
+ @JvmStatic
+ fun findAllWidgetsForPackageUser(
+ widgetPickerData: WidgetPickerData,
+ packageUserKey: PackageUserKey
+ ): List<WidgetItem> {
+ return findContentEntryForPackageUser(widgetPickerData, packageUserKey)?.mWidgets
+ ?: emptyList()
+ }
+
+ /**
+ * Finds and returns the [WidgetsListContentEntry] for the given package user.
+ *
+ * Set [fromDefaultWidgets] to true to limit the content entry to default widgets.
+ */
+ @JvmOverloads
+ @JvmStatic
+ fun findContentEntryForPackageUser(
+ widgetPickerData: WidgetPickerData,
+ packageUserKey: PackageUserKey,
+ fromDefaultWidgets: Boolean = false
+ ): WidgetsListContentEntry? {
+ val widgetsListBaseEntries =
+ if (fromDefaultWidgets) {
+ widgetPickerData.defaultWidgets
+ } else {
+ widgetPickerData.allWidgets
+ }
+
+ return widgetsListBaseEntries.filterIsInstance<WidgetsListContentEntry>().firstOrNull {
+ PackageUserKey.fromPackageItemInfo(it.mPkgItem) == packageUserKey
+ }
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
index 5a26087..d0aa7a8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
@@ -376,9 +376,10 @@
Utilities.rotateBounds(rect, 100, 100, 1)
assertEquals(Rect(70, 40, 80, 80), rect)
- rect = Rect(20, 70, 60, 80)
- Utilities.rotateBounds(rect, 100, 100, 2)
- assertEquals(Rect(40, 20, 80, 30), rect)
+ // case removed for b/28435189
+ // rect = Rect(20, 70, 60, 80)
+ // Utilities.rotateBounds(rect, 100, 100, 2)
+ // assertEquals(Rect(40, 20, 80, 30), rect)
rect = Rect(20, 70, 60, 80)
Utilities.rotateBounds(rect, 100, 100, 3)
diff --git a/tests/multivalentTests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java b/tests/multivalentTests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
index d2238ff..d938119 100644
--- a/tests/multivalentTests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/AlphabeticalAppsListTest.java
@@ -65,6 +65,7 @@
private static final int PRIVATE_SPACE_HEADER_ITEM_COUNT = 1;
private static final int MAIN_USER_APP_COUNT = 2;
private static final int PRIVATE_USER_APP_COUNT = 2;
+ private static final int VIEW_AT_END_OF_APP_LIST = 1;
private static final int NUM_APP_COLS = 4;
private static final int NUM_APP_ROWS = 3;
private static final int PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT = 1;
@@ -107,7 +108,8 @@
&& info.user.equals(MAIN_HANDLE));
assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT
- + PRIVATE_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size());
+ + PRIVATE_USER_APP_COUNT + VIEW_AT_END_OF_APP_LIST,
+ mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT,
mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size());
@@ -136,7 +138,7 @@
&& info.user.equals(MAIN_HANDLE));
assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT
- + PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT
+ + PRIVATE_SPACE_SYS_APP_SEPARATOR_ITEM_COUNT + VIEW_AT_END_OF_APP_LIST
+ PRIVATE_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT,
mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
@@ -166,7 +168,8 @@
mAlphabeticalAppsList.updateItemFilter(info -> info != null
&& info.user.equals(MAIN_HANDLE));
- assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT,
+ assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT +
+ VIEW_AT_END_OF_APP_LIST,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT, mAlphabeticalAppsList
.getAdapterItems().stream().filter(item ->
@@ -187,8 +190,8 @@
mAlphabeticalAppsList.updateItemFilter(info -> info != null
&& info.user.equals(MAIN_HANDLE));
- assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT,
- mAlphabeticalAppsList.getAdapterItems().size());
+ assertEquals(MAIN_USER_APP_COUNT + PRIVATE_SPACE_HEADER_ITEM_COUNT +
+ VIEW_AT_END_OF_APP_LIST, mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(PRIVATE_SPACE_HEADER_ITEM_COUNT, mAlphabeticalAppsList
.getAdapterItems().stream().filter(item ->
item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size());
@@ -206,7 +209,8 @@
mAlphabeticalAppsList.updateItemFilter(info -> info != null
&& info.user.equals(MAIN_HANDLE));
- assertEquals(MAIN_USER_APP_COUNT, mAlphabeticalAppsList.getAdapterItems().size());
+ assertEquals(MAIN_USER_APP_COUNT + VIEW_AT_END_OF_APP_LIST,
+ mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER).toList().size());
assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
@@ -222,7 +226,8 @@
mAlphabeticalAppsList.updateItemFilter(info -> info != null
&& info.user.equals(MAIN_HANDLE));
- assertEquals(2, mAlphabeticalAppsList.getAdapterItems().size());
+ assertEquals(MAIN_USER_APP_COUNT + VIEW_AT_END_OF_APP_LIST,
+ mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(0, mAlphabeticalAppsList.getAdapterItems().stream().filter(item ->
item.itemInfo != null
&& item.itemInfo.itemType == VIEW_TYPE_PRIVATE_SPACE_HEADER)
diff --git a/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTests.kt b/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTests.kt
new file mode 100644
index 0000000..ac2c553
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/allapps/FloatingHeaderViewTests.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.Flags
+import com.android.launcher3.util.ActivityContextWrapper
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class FloatingHeaderViewTests {
+
+ @get:Rule val mSetFlagsRule = SetFlagsRule()
+
+ private lateinit var context: Context
+ private lateinit var vut: FloatingHeaderView
+
+ @Before
+ fun setUp() {
+ context = ActivityContextWrapper(getApplicationContext())
+ // TODO(b/352161553): Inflate FloatingHeaderView or R.layout.all_apps_content with proper
+ // FloatingHeaderView#setup
+ vut = FloatingHeaderView(context)
+ vut.onFinishInflate()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_FLOATING_SEARCH_BAR, Flags.FLAG_MULTILINE_SEARCH_BAR)
+ fun onHeightUpdated_whenNotMultiline_thenZeroHeight() {
+ vut.setFloatingRowsCollapsed(true)
+ val beforeHeight = vut.maxTranslation
+ vut.updateSearchBarOffset(HEADER_HEIGHT_OFFSET)
+
+ vut.onHeightUpdated()
+
+ assertThat(vut.maxTranslation).isEqualTo(beforeHeight)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MULTILINE_SEARCH_BAR)
+ @DisableFlags(Flags.FLAG_FLOATING_SEARCH_BAR)
+ fun onHeightUpdated_whenMultiline_thenHeightIsOffset() {
+ vut.setFloatingRowsCollapsed(true)
+ vut.updateSearchBarOffset(HEADER_HEIGHT_OFFSET)
+
+ vut.onHeightUpdated()
+
+ assertThat(vut.maxTranslation).isEqualTo(HEADER_HEIGHT_OFFSET)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MULTILINE_SEARCH_BAR)
+ @EnableFlags(Flags.FLAG_FLOATING_SEARCH_BAR)
+ fun onHeightUpdated_whenFloatingRowsShownAndNotMultiline_thenAddsOnlyFloatingRow() {
+ // Collapse floating rows and expand to trigger header height calculation
+ vut.setFloatingRowsCollapsed(true)
+ vut.setFloatingRowsCollapsed(false)
+ val defaultHeight = vut.maxTranslation
+ vut.updateSearchBarOffset(HEADER_HEIGHT_OFFSET)
+
+ vut.onHeightUpdated()
+
+ assertThat(vut.maxTranslation).isEqualTo(defaultHeight)
+ }
+
+ companion object {
+ private const val HEADER_HEIGHT_OFFSET = 50
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt
index e8459d6..5bc57b0 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt
@@ -16,17 +16,15 @@
package com.android.launcher3.celllayout
-import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-@RunWith(AndroidJUnit4::class)
+// @RunWith(AndroidJUnit4::class) b/353965234
class CellLayoutMethodsTest {
@JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule()
- @Test
+ //@Test
fun pointToCellExact() {
val width = 1000
val height = 1000
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
index 71f7d47..ff545fe 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
@@ -186,6 +186,35 @@
assertThat(underTest.widgetsByComponentKey).isEmpty()
}
+ @Test
+ fun getWidgetsByPackageItem_returnsACopyOfMap() {
+ loadWidgets()
+
+ val latch = CountDownLatch(1)
+ Executors.MODEL_EXECUTOR.execute {
+ var update = true
+
+ // each "widgetsByPackageItem" read returns a different copy of the map held internally.
+ // Modifying one shouldn't impact another.
+ for ((_, _) in underTest.widgetsByPackageItem.entries) {
+ underTest.widgetsByPackageItem.clear()
+ if (update) { // trigger update
+ update = false
+ // Similarly, model could update its code independently while a client is
+ // iterating on the list.
+ underTest.update(app, /* packageUser= */ null)
+ }
+ }
+
+ latch.countDown()
+ }
+ if (!latch.await(LOAD_WIDGETS_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for test")
+ }
+
+ // No exception
+ }
+
private fun loadWidgets() {
val latch = CountDownLatch(1)
Executors.MODEL_EXECUTOR.execute {
diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/multivalentTests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
similarity index 95%
rename from tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
rename to tests/multivalentTests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
index 260f556..6cfa6ee 100644
--- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
@@ -22,9 +22,9 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyFloat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
@@ -41,6 +41,8 @@
import com.android.launcher3.testcomponent.TouchEventGenerator;
+import com.google.errorprone.annotations.FormatMethod;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,7 +54,8 @@
public class SingleAxisSwipeDetectorTest {
private static final String TAG = SingleAxisSwipeDetectorTest.class.getSimpleName();
- public static void L(String s, Object... parts) {
+ @FormatMethod
+ public static void logD(String s, Object... parts) {
Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
}
@@ -82,7 +85,7 @@
mTouchSlop = orgConfig.getScaledTouchSlop();
doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
- L("mTouchSlop=", mTouchSlop);
+ logD("mTouchSlop= %s", mTouchSlop);
}
@Test
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
index 1de99c5..d3e27b6 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SimpleBroadcastReceiverTest.kt
@@ -23,6 +23,7 @@
import android.os.Looper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
@@ -114,6 +115,7 @@
underTest = SimpleBroadcastReceiver(Handler(Looper.getMainLooper()), intentConsumer)
underTest.register(context, completionRunnable, 1, "test_action_1", "test_action_2")
+ getInstrumentation().waitForIdleSync()
verify(context).registerReceiver(same(underTest), intentFilterCaptor.capture(), eq(1))
verify(completionRunnable).run()
@@ -136,6 +138,7 @@
underTest = SimpleBroadcastReceiver(Handler(Looper.getMainLooper()), intentConsumer)
underTest.unregisterReceiverSafely(context)
+ getInstrumentation().waitForIdleSync()
verify(context).unregisterReceiver(same(underTest))
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
index 3f37563..71637f1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestSandboxModelContextWrapper.java
@@ -32,6 +32,7 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -57,6 +58,8 @@
protected ActivityAllAppsContainerView<ActivityContextWrapper> mAppsView;
private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
+ private final WidgetPickerDataProvider mWidgetPickerDataProvider =
+ new WidgetPickerDataProvider();
protected final UserCache mUserCache;
public TestSandboxModelContextWrapper(SandboxContext base) {
@@ -76,12 +79,19 @@
mAppsList = mAppsView.getPersonalAppList();
mAllAppsStore = mAppsView.getAppsStore();
}
+
@Nullable
@Override
public PopupDataProvider getPopupDataProvider() {
return mPopupDataProvider;
}
+ @Nullable
+ @Override
+ public WidgetPickerDataProvider getWidgetPickerDataProvider() {
+ return mWidgetPickerDataProvider;
+ }
+
@Override
public ActivityAllAppsContainerView<ActivityContextWrapper> getAppsView() {
return mAppsView;
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt
new file mode 100644
index 0000000..79b493a
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherAppWidgetHostTest.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.widget
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.Executors
+import java.util.function.IntConsumer
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertSame
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LauncherAppWidgetHostTest {
+
+ @Mock private lateinit var onAppWidgetRemovedCallback: IntConsumer
+
+ private val context = ActivityContextWrapper(getInstrumentation().targetContext)
+ private lateinit var underTest: LauncherAppWidgetHost
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest = LauncherAppWidgetHost(context, onAppWidgetRemovedCallback, emptyList())
+ }
+
+ @Test
+ fun `Host set view to recycle`() {
+ val mockRecycleView = mock(ListenableHostView::class.java)
+
+ assertNull(underTest.viewToRecycle)
+ underTest.recycleViewForNextCreation(mockRecycleView)
+
+ assertSame(mockRecycleView, underTest.viewToRecycle)
+ }
+
+ @Test
+ fun `Host create view`() {
+ val mockRecycleView = mock(ListenableHostView::class.java)
+
+ var resultView = underTest.onCreateView(context, WIDGET_ID, null)
+
+ assertNotSame(mockRecycleView, resultView)
+
+ underTest.recycleViewForNextCreation(mockRecycleView)
+ resultView = underTest.onCreateView(context, WIDGET_ID, null)
+
+ assertSame(mockRecycleView, resultView)
+ }
+
+ @Test
+ fun `Runnable called when app widget removed`() {
+ underTest.onAppWidgetRemoved(WIDGET_ID)
+
+ Executors.MODEL_EXECUTOR.submit {}.get()
+ getInstrumentation().waitForIdleSync()
+
+ verify(onAppWidgetRemovedCallback).accept(WIDGET_ID)
+ }
+
+ companion object {
+ const val WIDGET_ID = 10001
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
new file mode 100644
index 0000000..1822639
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/WidgetPickerDataProviderTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.widget.picker.model
+
+import android.content.ComponentName
+import android.content.Context
+import android.os.UserHandle
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import android.platform.test.rule.LimitDevicesRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.icons.ComponentWithLabel
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.model.data.PackageItemInfo
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.WidgetUtils
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.android.launcher3.widget.PendingAddWidgetInfo
+import com.android.launcher3.widget.model.WidgetsListBaseEntry
+import com.android.launcher3.widget.model.WidgetsListContentEntry
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider.WidgetPickerDataChangeListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+
+// Tests for the WidgetPickerDataProvider class
+
+@RunWith(AndroidJUnit4::class)
+@AllowedDevices(allowed = [DeviceProduct.ROBOLECTRIC])
+class WidgetPickerDataProviderTest {
+ @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var changeListener: WidgetPickerDataChangeListener
+
+ @Mock private lateinit var iconCache: IconCache
+
+ private lateinit var userHandle: UserHandle
+ private lateinit var context: Context
+ private lateinit var testInvariantProfile: InvariantDeviceProfile
+
+ private lateinit var appWidgetItem: WidgetItem
+
+ private var underTest = WidgetPickerDataProvider()
+
+ @Before
+ fun setUp() {
+ userHandle = UserHandle.CURRENT
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ testInvariantProfile = LauncherAppState.getIDP(context)
+
+ doAnswer { invocation: InvocationOnMock ->
+ val componentWithLabel = invocation.getArgument<Any>(0) as ComponentWithLabel
+ componentWithLabel.getComponent().shortClassName
+ }
+ .`when`(iconCache)
+ .getTitleNoCache(any<ComponentWithLabel>())
+
+ appWidgetItem = createWidgetItem()
+ }
+
+ @Test
+ fun setWidgets_invokesTheListener_andUpdatedWidgetsAvailable() {
+ assertThat(underTest.get().allWidgets).isEmpty()
+
+ underTest.setChangeListener(changeListener)
+ val allWidgets = appWidgetListBaseEntries()
+ underTest.setWidgets(allWidgets = allWidgets)
+
+ assertThat(underTest.get().allWidgets).containsExactlyElementsIn(allWidgets)
+ verify(changeListener, times(1)).onWidgetsBound()
+ verifyNoMoreInteractions(changeListener)
+ }
+
+ @Test
+ fun setWidgetRecommendations_callsBackTheListener_andUpdatedRecommendationsAvailable() {
+ underTest.setWidgets(allWidgets = appWidgetListBaseEntries())
+ assertThat(underTest.get().recommendations).isEmpty()
+
+ underTest.setChangeListener(changeListener)
+ val recommendations =
+ listOf(
+ PendingAddWidgetInfo(
+ appWidgetItem.widgetInfo,
+ LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION
+ ),
+ )
+ underTest.setWidgetRecommendations(recommendations)
+
+ assertThat(underTest.get().recommendations).hasSize(1)
+ verify(changeListener, times(1)).onRecommendedWidgetsBound()
+ verifyNoMoreInteractions(changeListener)
+ }
+
+ @Test
+ fun setChangeListener_null_noCallback() {
+ underTest.setChangeListener(changeListener)
+ underTest.setChangeListener(null) // reset
+
+ underTest.setWidgets(allWidgets = appWidgetListBaseEntries())
+ val recommendations =
+ listOf(
+ PendingAddWidgetInfo(
+ appWidgetItem.widgetInfo,
+ LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION
+ ),
+ )
+ underTest.setWidgetRecommendations(recommendations)
+
+ verifyNoMoreInteractions(changeListener)
+ }
+
+ private fun createWidgetItem(): WidgetItem {
+ val providerInfo =
+ WidgetUtils.createAppWidgetProviderInfo(
+ ComponentName.createRelative(APP_PACKAGE_NAME, APP_PROVIDER_1_CLASS_NAME)
+ )
+ val widgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo)
+ return WidgetItem(widgetInfo, testInvariantProfile, iconCache, context)
+ }
+
+ private fun appWidgetListBaseEntries(): List<WidgetsListBaseEntry> {
+ val packageItemInfo = PackageItemInfo(APP_PACKAGE_NAME, userHandle)
+ packageItemInfo.title = APP_PACKAGE_TITLE
+ val widgets = listOf(appWidgetItem)
+
+ return buildList {
+ add(WidgetsListHeaderEntry.create(packageItemInfo, APP_SECTION_NAME, widgets))
+ add(WidgetsListContentEntry(packageItemInfo, APP_SECTION_NAME, widgets))
+ }
+ }
+
+ companion object {
+ const val APP_PACKAGE_NAME = "com.example.app"
+ const val APP_PACKAGE_TITLE = "SomeApp"
+ const val APP_SECTION_NAME = "S" // for fast popup
+ const val APP_PROVIDER_1_CLASS_NAME = "appProvider1"
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt
new file mode 100644
index 0000000..e59e211
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/model/data/WidgetPickerDataTest.kt
@@ -0,0 +1,379 @@
+/*
+ * 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.widget.picker.model.data
+
+import android.content.ComponentName
+import android.content.Context
+import android.os.UserHandle
+import android.platform.test.rule.AllowedDevices
+import android.platform.test.rule.DeviceProduct
+import android.platform.test.rule.LimitDevicesRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION
+import com.android.launcher3.icons.ComponentWithLabel
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.PackageItemInfo
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.PackageUserKey
+import com.android.launcher3.util.WidgetUtils
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.android.launcher3.widget.PendingAddWidgetInfo
+import com.android.launcher3.widget.model.WidgetsListBaseEntry
+import com.android.launcher3.widget.model.WidgetsListContentEntry
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry
+import com.android.launcher3.widget.picker.WidgetRecommendationCategory
+import com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY
+import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.findAllWidgetsForPackageUser
+import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.findContentEntryForPackageUser
+import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withRecommendedWidgets
+import com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.withWidgets
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+
+// Tests for code / classes in WidgetPickerData file.
+
+@RunWith(AndroidJUnit4::class)
+@AllowedDevices(allowed = [DeviceProduct.ROBOLECTRIC])
+class WidgetPickerDataTest {
+ @Rule @JvmField val limitDevicesRule = LimitDevicesRule()
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var iconCache: IconCache
+
+ private lateinit var userHandle: UserHandle
+ private lateinit var context: Context
+ private lateinit var testInvariantProfile: InvariantDeviceProfile
+
+ private lateinit var app1PackageItemInfo: PackageItemInfo
+ private lateinit var app2PackageItemInfo: PackageItemInfo
+
+ private lateinit var app1WidgetItem1: WidgetItem
+ private lateinit var app1WidgetItem2: WidgetItem
+ private lateinit var app2WidgetItem1: WidgetItem
+
+ @Before
+ fun setUp() {
+ userHandle = UserHandle.CURRENT
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ testInvariantProfile = LauncherAppState.getIDP(context)
+
+ doAnswer { invocation: InvocationOnMock ->
+ val componentWithLabel = invocation.getArgument<Any>(0) as ComponentWithLabel
+ componentWithLabel.getComponent().shortClassName
+ }
+ .`when`(iconCache)
+ .getTitleNoCache(any<ComponentWithLabel>())
+
+ app1PackageItemInfo = packageItemInfoWithTitle(APP_1_PACKAGE_NAME, APP_1_PACKAGE_TITLE)
+ app2PackageItemInfo = packageItemInfoWithTitle(APP_2_PACKAGE_NAME, APP_2_PACKAGE_TITLE)
+
+ app1WidgetItem1 = createWidgetItem(APP_1_PACKAGE_NAME, APP_1_PROVIDER_1_CLASS_NAME)
+ app1WidgetItem2 = createWidgetItem(APP_1_PACKAGE_NAME, APP_1_PROVIDER_2_CLASS_NAME)
+ app2WidgetItem1 = createWidgetItem(APP_2_PACKAGE_NAME, APP_2_PROVIDER_1_CLASS_NAME)
+ }
+
+ @Test
+ fun withWidgets_returnsACopyWithProvidedWidgets() {
+ // only app two
+ val widgetPickerData = WidgetPickerData(allWidgets = appTwoWidgetsListBaseEntries())
+
+ // update: only app 1 and default list set
+ val newAllWidgets: List<WidgetsListBaseEntry> =
+ appOneWidgetsListBaseEntries(includeWidgetTwo = true)
+ val newDefaultWidgets: List<WidgetsListBaseEntry> =
+ appOneWidgetsListBaseEntries(includeWidgetTwo = false)
+
+ val newWidgetData = widgetPickerData.withWidgets(newAllWidgets, newDefaultWidgets)
+
+ assertThat(newWidgetData.allWidgets).containsExactlyElementsIn(newAllWidgets)
+ assertThat(newWidgetData.defaultWidgets).containsExactlyElementsIn(newDefaultWidgets)
+ }
+
+ @Test
+ fun withWidgets_noExplicitDefaults_unsetsOld() {
+ // only app two
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets = appTwoWidgetsListBaseEntries(),
+ defaultWidgets = appTwoWidgetsListBaseEntries()
+ )
+
+ val newWidgetData =
+ widgetPickerData.withWidgets(allWidgets = appOneWidgetsListBaseEntries())
+
+ assertThat(newWidgetData.allWidgets)
+ .containsExactlyElementsIn(appOneWidgetsListBaseEntries())
+ assertThat(newWidgetData.defaultWidgets).isEmpty() // previous values cleared.
+ }
+
+ @Test
+ fun withRecommendedWidgets_returnsACopyWithProvidedRecommendedWidgets() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries())
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets = buildList { appTwoWidgetsListBaseEntries() }
+ )
+ val recommendations: List<ItemInfo> =
+ listOf(
+ PendingAddWidgetInfo(
+ app1WidgetItem1.widgetInfo,
+ CONTAINER_WIDGETS_PREDICTION,
+ CATEGORY_1
+ ),
+ PendingAddWidgetInfo(
+ app2WidgetItem1.widgetInfo,
+ CONTAINER_WIDGETS_PREDICTION,
+ CATEGORY_2
+ ),
+ )
+
+ val updatedData = widgetPickerData.withRecommendedWidgets(recommendations)
+
+ assertThat(updatedData.recommendations.keys).containsExactly(CATEGORY_1, CATEGORY_2)
+ assertThat(updatedData.recommendations[CATEGORY_1]).containsExactly(app1WidgetItem1)
+ assertThat(updatedData.recommendations[CATEGORY_2]).containsExactly(app2WidgetItem1)
+ }
+
+ @Test
+ fun withRecommendedWidgets_noCategory_usesDefault() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries())
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets = buildList { appTwoWidgetsListBaseEntries() }
+ )
+ val recommendations: List<ItemInfo> =
+ listOf(
+ PendingAddWidgetInfo(app1WidgetItem1.widgetInfo, CONTAINER_WIDGETS_PREDICTION),
+ PendingAddWidgetInfo(app2WidgetItem1.widgetInfo, CONTAINER_WIDGETS_PREDICTION),
+ )
+
+ val updatedData = widgetPickerData.withRecommendedWidgets(recommendations)
+
+ assertThat(updatedData.recommendations.keys)
+ .containsExactly(DEFAULT_WIDGET_RECOMMENDATION_CATEGORY)
+ assertThat(updatedData.recommendations[DEFAULT_WIDGET_RECOMMENDATION_CATEGORY])
+ .containsExactly(app1WidgetItem1, app2WidgetItem1)
+ }
+
+ @Test
+ fun withRecommendedWidgets_emptyRecommendations_clearsOld() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries())
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets = buildList { appTwoWidgetsListBaseEntries() },
+ recommendations = mapOf(CATEGORY_1 to listOf(app1WidgetItem1))
+ )
+
+ val updatedData = widgetPickerData.withRecommendedWidgets(listOf())
+
+ assertThat(updatedData.recommendations).isEmpty()
+ }
+
+ @Test
+ fun withRecommendedWidgets_widgetNotInAllWidgets_filteredOut() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false))
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets = buildList { appTwoWidgetsListBaseEntries() },
+ )
+
+ val recommendations: List<ItemInfo> =
+ listOf(
+ PendingAddWidgetInfo(app1WidgetItem2.widgetInfo, CONTAINER_WIDGETS_PREDICTION),
+ PendingAddWidgetInfo(app2WidgetItem1.widgetInfo, CONTAINER_WIDGETS_PREDICTION),
+ )
+ val updatedData = widgetPickerData.withRecommendedWidgets(recommendations)
+
+ assertThat(updatedData.recommendations).hasSize(1)
+ // no app1widget2
+ assertThat(updatedData.recommendations.values.first()).containsExactly(app2WidgetItem1)
+ }
+
+ @Test
+ fun findContentEntryForPackageUser_returnsCorrectEntry() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries())
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets = buildList { addAll(appTwoWidgetsListBaseEntries()) }
+ )
+ val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
+
+ val contentEntry = findContentEntryForPackageUser(widgetPickerData, app1PackageUserKey)
+
+ assertThat(contentEntry).isNotNull()
+ assertThat(contentEntry?.mPkgItem).isEqualTo(app1PackageItemInfo)
+ assertThat(contentEntry?.mWidgets).hasSize(2)
+ }
+
+ @Test
+ fun findContentEntryForPackageUser_fromDefaults_returnsEntryFromDefaultWidgets() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries())
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets =
+ buildList { addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false)) }
+ )
+ val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
+
+ val contentEntry =
+ findContentEntryForPackageUser(
+ widgetPickerData = widgetPickerData,
+ packageUserKey = app1PackageUserKey,
+ fromDefaultWidgets = true
+ )
+
+ assertThat(contentEntry).isNotNull()
+ assertThat(contentEntry?.mPkgItem).isEqualTo(app1PackageItemInfo)
+ // only one widget (since default widgets had only one widget for app A
+ assertThat(contentEntry?.mWidgets).hasSize(1)
+ }
+
+ @Test
+ fun findContentEntryForPackageUser_noMatch_returnsNull() {
+ val app2PackageUserKey = PackageUserKey.fromPackageItemInfo(app2PackageItemInfo)
+ val widgetPickerData =
+ WidgetPickerData(allWidgets = buildList { addAll(appOneWidgetsListBaseEntries()) })
+
+ val contentEntry = findContentEntryForPackageUser(widgetPickerData, app2PackageUserKey)
+
+ assertThat(contentEntry).isNull()
+ }
+
+ @Test
+ fun findAllWidgetsForPackageUser_returnsListOfWidgets() {
+ val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets =
+ buildList {
+ addAll(appOneWidgetsListBaseEntries())
+ addAll(appTwoWidgetsListBaseEntries())
+ },
+ defaultWidgets =
+ buildList { addAll(appOneWidgetsListBaseEntries(includeWidgetTwo = false)) }
+ )
+
+ val widgets = findAllWidgetsForPackageUser(widgetPickerData, app1PackageUserKey)
+
+ // both widgets returned irrespective of default widgets list
+ assertThat(widgets).hasSize(2)
+ }
+
+ @Test
+ fun findAllWidgetsForPackageUser_noMatch_returnsEmptyList() {
+ val widgetPickerData =
+ WidgetPickerData(
+ allWidgets = buildList { addAll(appTwoWidgetsListBaseEntries()) },
+ )
+ val app1PackageUserKey = PackageUserKey.fromPackageItemInfo(app1PackageItemInfo)
+
+ val widgets = findAllWidgetsForPackageUser(widgetPickerData, app1PackageUserKey)
+
+ assertThat(widgets).isEmpty()
+ }
+
+ private fun packageItemInfoWithTitle(packageName: String, title: String): PackageItemInfo {
+ val packageItemInfo = PackageItemInfo(packageName, userHandle)
+ packageItemInfo.title = title
+ return packageItemInfo
+ }
+
+ private fun createWidgetItem(packageName: String, widgetProviderName: String): WidgetItem {
+ val providerInfo =
+ WidgetUtils.createAppWidgetProviderInfo(
+ ComponentName.createRelative(packageName, widgetProviderName)
+ )
+ val widgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo)
+ return WidgetItem(widgetInfo, testInvariantProfile, iconCache, context)
+ }
+
+ private fun appTwoWidgetsListBaseEntries(): List<WidgetsListBaseEntry> = buildList {
+ val widgets = listOf(app2WidgetItem1)
+ add(WidgetsListHeaderEntry.create(app2PackageItemInfo, APP_2_SECTION_NAME, widgets))
+ add(WidgetsListContentEntry(app2PackageItemInfo, APP_2_SECTION_NAME, widgets))
+ }
+
+ private fun appOneWidgetsListBaseEntries(
+ includeWidgetTwo: Boolean = true
+ ): List<WidgetsListBaseEntry> = buildList {
+ val widgets =
+ if (includeWidgetTwo) {
+ listOf(app1WidgetItem1, app1WidgetItem2)
+ } else {
+ listOf(app1WidgetItem1)
+ }
+
+ add(WidgetsListHeaderEntry.create(app1PackageItemInfo, APP_1_SECTION_NAME, widgets))
+ add(WidgetsListContentEntry(app1PackageItemInfo, APP_1_SECTION_NAME, widgets))
+ }
+
+ companion object {
+ private const val APP_1_PACKAGE_NAME = "com.example.app1"
+ private const val APP_1_PACKAGE_TITLE = "App1"
+ private const val APP_1_SECTION_NAME = "A" // for fast popup
+ private const val APP_1_PROVIDER_1_CLASS_NAME = "app1Provider1"
+ private const val APP_1_PROVIDER_2_CLASS_NAME = "app1Provider2"
+
+ private const val APP_2_PACKAGE_NAME = "com.example.app2"
+ private const val APP_2_PACKAGE_TITLE = "SomeApp2"
+ private const val APP_2_SECTION_NAME = "S" // for fast popup
+ private const val APP_2_PROVIDER_1_CLASS_NAME = "app2Provider1"
+
+ private val CATEGORY_1 =
+ WidgetRecommendationCategory(/* categoryTitleRes= */ 0, /* order= */ 0)
+ private val CATEGORY_2 =
+ WidgetRecommendationCategory(/* categoryTitleRes= */ 1, /* order= */ 1)
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt b/tests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
new file mode 100644
index 0000000..cf03adc
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/FloatingMaskViewTest.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.view.ViewGroup
+import android.view.ViewGroup.MarginLayoutParams
+import android.widget.ImageView
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.util.ActivityContextWrapper
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+class FloatingMaskViewTest {
+ @Mock
+ private val mockAllAppsRecyclerView: AllAppsRecyclerView? = null
+
+ @Mock
+ private val mockBottomBox: ImageView? = null
+ private var mVut: FloatingMaskView? = null
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val context: Context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ mVut = FloatingMaskView(context)
+ mVut!!.layoutParams = MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+
+ @Test
+ fun setParameters_paramsMarginEqualRecyclerViewPadding() {
+ val floatingMaskView = Mockito.spy(mVut)
+ Mockito.`when`(mockAllAppsRecyclerView!!.paddingLeft).thenReturn(PADDING_PX)
+ Mockito.`when`(mockAllAppsRecyclerView.paddingRight).thenReturn(PADDING_PX)
+ Mockito.`when`(mockAllAppsRecyclerView.paddingBottom).thenReturn(PADDING_PX)
+ Mockito.`when`(floatingMaskView!!.bottomBox).thenReturn(mockBottomBox)
+ val lp = floatingMaskView.layoutParams as MarginLayoutParams
+
+ floatingMaskView.setParameters(lp, mockAllAppsRecyclerView)
+
+ Truth.assertThat(lp.leftMargin).isEqualTo(PADDING_PX)
+ Truth.assertThat(lp.rightMargin).isEqualTo(PADDING_PX)
+ Mockito.verify(mockBottomBox)?.minimumHeight = PADDING_PX
+ }
+
+ companion object {
+ private const val PADDING_PX = 15
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
index de48432..398f9c5 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
@@ -86,6 +86,8 @@
private static final String CAMERA_PACKAGE_NAME = "com.android.launcher3.tests.camera";
private static final int CONTAINER_HEADER_ELEMENT_COUNT = 1;
private static final int LOCK_UNLOCK_BUTTON_COUNT = 1;
+ private static final int MAIN_USER_APP_COUNT = 1;
+ private static final int VIEW_AT_END_OF_APP_LIST = 1;
private static final int PS_SETTINGS_BUTTON_COUNT_VISIBLE = 1;
private static final int PS_SETTINGS_BUTTON_COUNT_INVISIBLE = 0;
private static final int PS_TRANSITION_IMAGE_COUNT = 1;
@@ -300,8 +302,8 @@
int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
- // The number of adapterItems should be the private space apps + one main app + header.
- assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
+ assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT
+ + CONTAINER_HEADER_ELEMENT_COUNT + VIEW_AT_END_OF_APP_LIST,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(position,
privateProfileManager.scrollForHeaderToBeVisibleInContainer(
@@ -335,8 +337,8 @@
int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT) - 1;
int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
- // The number of adapterItems should be the private space apps + one main app + header.
- assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
+ assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT
+ + CONTAINER_HEADER_ELEMENT_COUNT + VIEW_AT_END_OF_APP_LIST,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(position,
privateProfileManager.scrollForHeaderToBeVisibleInContainer(
@@ -370,8 +372,8 @@
int rows = (int) (ALL_APPS_HEIGHT - BIGGER_PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
- // The number of adapterItems should be the private space apps + one main app + header.
- assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
+ assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT
+ + CONTAINER_HEADER_ELEMENT_COUNT + VIEW_AT_END_OF_APP_LIST,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(position,
privateProfileManager.scrollForHeaderToBeVisibleInContainer(
@@ -399,8 +401,7 @@
mAlphabeticalAppsList.updateItemFilter(info -> info != null
&& info.user.equals(MAIN_HANDLE));
- // The number of adapterItems should be the private space apps + one main app.
- assertEquals(NUM_PRIVATE_SPACE_APPS + 1,
+ assertEquals(NUM_PRIVATE_SPACE_APPS + MAIN_USER_APP_COUNT + VIEW_AT_END_OF_APP_LIST,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(SCROLL_NO_WHERE, privateProfileManager.scrollForHeaderToBeVisibleInContainer(
new AllAppsRecyclerView(mContext),
diff --git a/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt b/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
new file mode 100644
index 0000000..b491f17
--- /dev/null
+++ b/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.folder
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.folder.FolderNameInfos.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+data class Label(val index: Int, val label: String, val score: Float)
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FolderNameInfosTest {
+
+ companion object {
+ val statusList =
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ }
+
+ @Test
+ fun status() {
+ assertStatus(statusList)
+ assertStatus(
+ listOf(
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ )
+ assertStatus(
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ )
+ )
+ assertStatus(
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ )
+ )
+ }
+
+ fun assertStatus(statusList: List<Int>) {
+ var infos = FolderNameInfos()
+ statusList.forEach { infos.setStatus(it) }
+ assert(infos.status() == statusList.sum()) {
+ "There is an overlap on the status constants!"
+ }
+ }
+
+ @Test
+ fun hasPrimary() {
+ assertHasPrimary(
+ createNameInfos(listOf(Label(0, "label", 1f)), statusList),
+ hasPrimary = true
+ )
+ assertHasPrimary(
+ createNameInfos(listOf(Label(1, "label", 1f)), statusList),
+ hasPrimary = false
+ )
+ assertHasPrimary(
+ createNameInfos(
+ listOf(Label(0, "label", 1f)),
+ listOf(
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ ),
+ hasPrimary = false
+ )
+ }
+
+ private fun assertHasPrimary(nameInfos: FolderNameInfos, hasPrimary: Boolean) =
+ assert(nameInfos.hasPrimary() == hasPrimary)
+
+ private fun createNameInfos(labels: List<Label>?, statusList: List<Int>?): FolderNameInfos {
+ val infos = FolderNameInfos()
+ labels?.forEach { infos.setLabel(it.index, it.label, it.score) }
+ statusList?.forEach { infos.setStatus(it) }
+ return infos
+ }
+
+ @Test
+ fun hasSuggestions() {
+ assertHasSuggestions(
+ createNameInfos(listOf(Label(0, "label", 1f)), null),
+ hasSuggestions = true
+ )
+ assertHasSuggestions(createNameInfos(null, null), hasSuggestions = false)
+ // There is a max of 4 suggestions
+ assertHasSuggestions(
+ createNameInfos(listOf(Label(5, "label", 1f)), null),
+ hasSuggestions = false
+ )
+ assertHasSuggestions(
+ createNameInfos(
+ listOf(
+ Label(0, "label", 1f),
+ Label(1, "label", 1f),
+ Label(2, "label", 1f),
+ Label(3, "label", 1f)
+ ),
+ null
+ ),
+ hasSuggestions = true
+ )
+ }
+
+ private fun assertHasSuggestions(nameInfos: FolderNameInfos, hasSuggestions: Boolean) =
+ assert(nameInfos.hasSuggestions() == hasSuggestions)
+
+ @Test
+ fun hasContains() {
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "label3", 1f),
+ Label(3, "label4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "label3", -1f),
+ contains = true
+ )
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "label3", 1f),
+ Label(3, "label4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "label5", -1f),
+ contains = false
+ )
+ assertContains(
+ createNameInfos(null, null),
+ label = Label(-1, "label1", -1f),
+ contains = false
+ )
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "lAbel3", 1f),
+ Label(3, "lEbel4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "LaBEl3", -1f),
+ contains = true
+ )
+ }
+
+ private fun assertContains(nameInfos: FolderNameInfos, label: Label, contains: Boolean) =
+ assert(nameInfos.contains(label.label) == contains)
+}
diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index da14425..5516f45 100644
--- a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -23,7 +23,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.THEMED_ICONS
import com.android.launcher3.LauncherPrefs.Companion.get
import com.android.launcher3.icons.BaseIconFactory
import com.android.launcher3.icons.FastBitmapDrawable
@@ -51,6 +51,8 @@
private lateinit var modelHelper: LauncherModelHelper
private lateinit var folderIcon: FolderIcon
+ private var defaultThemedIcons = false
+
@Before
fun setup() {
getInstrumentation().runOnMainSync {
@@ -127,16 +129,20 @@
previewItemManager.mIconSize
)
)
+
+ defaultThemedIcons = get(context).get(THEMED_ICONS)
}
+
@After
@Throws(Exception::class)
fun tearDown() {
+ get(context).put(THEMED_ICONS, defaultThemedIcons)
modelHelper.destroy()
}
@Test
fun checkThemedIconWithThemingOn_iconShouldBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, true)
+ get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[0])
@@ -146,7 +152,7 @@
@Test
fun checkThemedIconWithThemingOff_iconShouldNotBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, false)
+ get(context).put(THEMED_ICONS, false)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[0])
@@ -156,7 +162,7 @@
@Test
fun checkUnthemedIconWithThemingOn_iconShouldNotBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, true)
+ get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[1])
@@ -166,7 +172,7 @@
@Test
fun checkUnthemedIconWithThemingOff_iconShouldNotBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, false)
+ get(context).put(THEMED_ICONS, false)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[1])
@@ -176,7 +182,7 @@
@Test
fun checkThemedIconWithBadgeWithThemingOn_iconAndBadgeShouldBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, true)
+ get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[2])
@@ -189,7 +195,7 @@
@Test
fun checkUnthemedIconWithBadgeWithThemingOn_badgeShouldBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, true)
+ get(context).put(THEMED_ICONS, true)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[3])
@@ -202,7 +208,7 @@
@Test
fun checkUnthemedIconWithBadgeWithThemingOff_iconAndBadgeShouldNotBeThemed() {
- get(context).put(LauncherPrefs.THEMED_ICONS, false)
+ get(context).put(THEMED_ICONS, false)
val drawingParams = PreviewItemDrawingParams(0f, 0f, 0f)
previewItemManager.setDrawable(drawingParams, folderItems[3])
diff --git a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
index 58b915f..7182cf3 100644
--- a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
+++ b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
@@ -94,7 +94,13 @@
val srcCountMap = itemsToMap(srcGrid.items)
val resultCountMap = itemsToMap(resultItems)
- val diff = resultCountMap - srcCountMap
+ val diff = resultCountMap.toMutableMap()
+ for ((srcKey, srcValue) in srcCountMap) {
+ val destValue = diff[srcKey]
+ if (destValue != null) {
+ diff[srcKey] = destValue - srcValue
+ }
+ }
diff.forEach { (k, count) ->
assert(count >= 0) { "Source item $k not present on the result" }
diff --git a/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt b/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt
new file mode 100644
index 0000000..9a8f957
--- /dev/null
+++ b/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.pageindicators
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.util.ActivityContextWrapper
+import junit.framework.TestCase.assertEquals
+import org.junit.Test
+import org.mockito.Mockito
+
+class PageIndicatorDotsTest {
+
+ private val context: Context =
+ ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ private val pageIndicatorDots: PageIndicatorDots = Mockito.spy(PageIndicatorDots(context))
+
+ @Test
+ fun `setActiveMarker should set the active page to the parameter passed`() {
+ pageIndicatorDots.setActiveMarker(2)
+
+ assertEquals(2, pageIndicatorDots.activePage)
+ }
+
+ @Test
+ fun `setActiveMarker should set the active page to the parameter passed divided by two in two panel layouts`() {
+ pageIndicatorDots.mIsTwoPanels = true
+
+ pageIndicatorDots.setActiveMarker(5)
+
+ assertEquals(2, pageIndicatorDots.activePage)
+ }
+
+ @Test
+ fun `setMarkersCount should set the number of pages to the passed parameter and if the last page gets removed we want to go to the previous page`() {
+ pageIndicatorDots.setMarkersCount(3)
+
+ assertEquals(3, pageIndicatorDots.numPages)
+ }
+
+ @Test
+ fun `for setMarkersCount if the last page gets removed we want to go to the previous page`() {
+ pageIndicatorDots.setActiveMarker(2)
+
+ pageIndicatorDots.setMarkersCount(2)
+
+ assertEquals(1, pageIndicatorDots.activePage)
+ assertEquals(pageIndicatorDots.activePage.toFloat(), pageIndicatorDots.currentPosition)
+ }
+}
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
new file mode 100644
index 0000000..b531adb
--- /dev/null
+++ b/tests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
@@ -0,0 +1,228 @@
+/*
+ * 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.pm
+
+import android.content.pm.LauncherApps
+import android.content.pm.PackageInstaller
+import android.os.Build
+import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.PackageUserKey
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InstallSessionTrackerTest {
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ private val mockInstallSessionHelper: InstallSessionHelper = mock()
+ private val mockCallback: InstallSessionTracker.Callback = mock()
+ private val mockPackageInstaller: PackageInstaller = mock()
+
+ private val launcherModelHelper = LauncherModelHelper()
+ private val sandboxContext = launcherModelHelper.sandboxContext
+
+ lateinit var launcherApps: LauncherApps
+ lateinit var installSessionTracker: InstallSessionTracker
+
+ @Before
+ fun setup() {
+ launcherApps = sandboxContext.spyService(LauncherApps::class.java)
+ installSessionTracker =
+ InstallSessionTracker(
+ mockInstallSessionHelper,
+ mockCallback,
+ mockPackageInstaller,
+ launcherApps
+ )
+ }
+
+ @After
+ fun teardown() {
+ launcherModelHelper.destroy()
+ }
+
+ @Test
+ fun `onCreated triggers callbacks for setting up new install session`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onCreated(expectedSessionId)
+ // Then
+ verify(mockCallback).onInstallSessionCreated(any())
+ verify(mockCallback).onUpdateSessionDisplay(expectedPackageKey, expectedSession)
+ verify(mockInstallSessionHelper).tryQueuePromiseAppIcon(expectedSession)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
+ fun `onCreated for unarchival triggers onPackageStateChanged`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ spy(PackageInstaller.SessionInfo()).apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ whenever(isUnarchival).thenReturn(true)
+ }
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onCreated(expectedSessionId)
+ // Then
+ verify(mockCallback).onPackageStateChanged(any())
+ }
+
+ @Test
+ fun `onFinished triggers onPackageStateChanged if session found in cache`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ whenever(mockInstallSessionHelper.activeSessions)
+ .thenReturn(hashMapOf(expectedPackageKey to expectedSession))
+ // When
+ installSessionTracker.onFinished(expectedSessionId, /* success */ true)
+ // Then
+ verify(mockCallback).onPackageStateChanged(any())
+ }
+
+ @Test
+ fun `onFinished failure calls onSessionFailure and promise icon removal for existing icon`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedPackage = "appPackageName"
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = expectedPackage
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey(expectedPackage, UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ whenever(mockInstallSessionHelper.activeSessions)
+ .thenReturn(hashMapOf(expectedPackageKey to expectedSession))
+ whenever(mockInstallSessionHelper.promiseIconAddedForId(expectedSessionId)).thenReturn(true)
+ // When
+ installSessionTracker.onFinished(expectedSessionId, /* success */ false)
+ // Then
+ verify(mockCallback).onSessionFailure(expectedPackage, expectedPackageKey.mUser)
+ verify(mockInstallSessionHelper).removePromiseIconId(expectedSessionId)
+ }
+
+ @Test
+ fun `onProgressChanged triggers onPackageStateChanged if verified session found`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onProgressChanged(expectedSessionId, /* progress */ 50f)
+ // Then
+ verify(mockCallback).onPackageStateChanged(any())
+ }
+
+ @Test
+ fun `onBadgingChanged triggers session display update and queues promise icon if verified`() {
+ // Given
+ val expectedSessionId = 1
+ val expectedSession =
+ PackageInstaller.SessionInfo().apply {
+ sessionId = expectedSessionId
+ appPackageName = "appPackageName"
+ userId = 0
+ }
+ val expectedPackageKey = PackageUserKey("appPackageName", UserHandle(0))
+ whenever(mockInstallSessionHelper.getVerifiedSessionInfo(expectedSessionId))
+ .thenReturn(expectedSession)
+ // When
+ installSessionTracker.onBadgingChanged(expectedSessionId)
+ // Then
+ verify(mockCallback).onUpdateSessionDisplay(expectedPackageKey, expectedSession)
+ verify(mockInstallSessionHelper).tryQueuePromiseAppIcon(expectedSession)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ fun `register triggers registerPackageInstallerSessionCallback for versions from Q`() {
+ // Given
+ whenever(
+ launcherApps.registerPackageInstallerSessionCallback(
+ MODEL_EXECUTOR,
+ installSessionTracker
+ )
+ )
+ .then { /* no-op */ }
+ // When
+ installSessionTracker.register()
+ // Then
+ verify(launcherApps)
+ .registerPackageInstallerSessionCallback(MODEL_EXECUTOR, installSessionTracker)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+ fun `unregister triggers unregisterPackageInstallerSessionCallback for versions from Q`() {
+ // Given
+ whenever(launcherApps.unregisterPackageInstallerSessionCallback(installSessionTracker))
+ .then { /* no-op */ }
+ // When
+ installSessionTracker.unregister()
+ // Then
+ verify(launcherApps).unregisterPackageInstallerSessionCallback(installSessionTracker)
+ }
+}
diff --git a/tests/src/com/android/launcher3/pm/UserCacheTest.kt b/tests/src/com/android/launcher3/pm/UserCacheTest.kt
new file mode 100644
index 0000000..b21219e
--- /dev/null
+++ b/tests/src/com/android/launcher3/pm/UserCacheTest.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.pm
+
+import android.os.Process.myUserHandle
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.TestUtil
+import com.android.launcher3.util.UserIconInfo
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class UserCacheTest {
+ private val launcherModelHelper = LauncherModelHelper()
+ private val sandboxContext = launcherModelHelper.sandboxContext
+ private lateinit var userCache: UserCache
+
+ @Before
+ fun setup() {
+ userCache = UserCache.getInstance(sandboxContext)
+ }
+
+ @After
+ fun teardown() {
+ launcherModelHelper.destroy()
+ }
+
+ @Test
+ fun `getBadgeDrawable only returns a UserBadgeDrawable given a user in the cache`() {
+ // Given
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_WORK)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(myUserHandle(), expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualDrawable = UserCache.getBadgeDrawable(sandboxContext, myUserHandle())
+ val unexpectedDrawable = UserCache.getBadgeDrawable(sandboxContext, UserHandle(66))
+ // Then
+ assertThat(actualDrawable).isNotNull()
+ assertThat(unexpectedDrawable).isNull()
+ }
+
+ @Test
+ fun `getPreInstallApps returns list of pre installed apps given a user`() {
+ // Given
+ val expectedApps = listOf("Google")
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToPreInstallCache(myUserHandle(), expectedApps)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualApps = userCache.getPreInstallApps(myUserHandle())
+ // Then
+ assertThat(actualApps).isEqualTo(expectedApps)
+ }
+
+ @Test
+ fun `getUserProfiles returns copy of UserCache profiles`() {
+ // Given
+ val expectedProfiles = listOf(myUserHandle())
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_MAIN)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(myUserHandle(), expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualProfiles = userCache.userProfiles
+ // Then
+ assertThat(actualProfiles).isEqualTo(expectedProfiles)
+ }
+
+ @Test
+ fun `getUserForSerialNumber returns user key matching given entry serial number`() {
+ // Given
+ val expectedSerial = 42L
+ val expectedProfile = UserHandle(42)
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_MAIN, expectedSerial)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(expectedProfile, expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualProfile = userCache.getUserForSerialNumber(expectedSerial)
+ // Then
+ assertThat(actualProfile).isEqualTo(expectedProfile)
+ }
+
+ @Test
+ fun `getUserInfo returns cached UserIconInfo given user key`() {
+ // Given
+ val expectedProfile = UserHandle(1)
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_WORK)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(expectedProfile, expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualIconInfo = userCache.getUserInfo(expectedProfile)
+ // Then
+ assertThat(actualIconInfo).isEqualTo(expectedIconInfo)
+ }
+
+ @Test
+ fun `getSerialNumberForUser returns cached UserIconInfo serial number given user key`() {
+ // Given
+ val expectedSerial = 42L
+ val expectedProfile = UserHandle(1)
+ val expectedIconInfo = UserIconInfo(myUserHandle(), UserIconInfo.TYPE_WORK, expectedSerial)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ userCache.putToCache(expectedProfile, expectedIconInfo)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // When
+ val actualSerial = userCache.getSerialNumberForUser(expectedProfile)
+ // Then
+ assertThat(actualSerial).isEqualTo(expectedSerial)
+ }
+}
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index 98b6b4b..dcfcad5 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -63,6 +63,8 @@
import com.android.launcher3.util.TestSandboxModelContextWrapper;
import com.android.launcher3.util.UserIconInfo;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
+import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
import org.junit.After;
import org.junit.Assert;
@@ -73,8 +75,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-
@SmallTest
@RunWith(LauncherMultivalentJUnit.class)
public class SystemShortcutTest {
@@ -86,7 +86,7 @@
private TestSandboxModelContextWrapper mTestContext;
private final SandboxModelContext mSandboxContext = new SandboxModelContext();
private PrivateProfileManager mPrivateProfileManager;
- private PopupDataProvider mPopupDataProvider;
+ private WidgetPickerDataProvider mWidgetPickerDataProvider;
private AppInfo mAppInfo;
@Mock UserCache mUserCache;
@Mock ApiWrapper mApiWrapper;
@@ -119,8 +119,8 @@
spyOn(mPrivateProfileManager);
when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);
- mPopupDataProvider = mTestContext.getPopupDataProvider();
- spyOn(mPopupDataProvider);
+ mWidgetPickerDataProvider = mTestContext.getWidgetPickerDataProvider();
+ spyOn(mWidgetPickerDataProvider);
}
@After
@@ -141,7 +141,7 @@
mAppInfo = new AppInfo();
mAppInfo.componentName = new ComponentName(mTestContext, getClass());
assertNotNull(mAppInfo.getTargetComponent());
- doReturn(new ArrayList<>()).when(mPopupDataProvider).getWidgetsForPackageUser(any());
+ doReturn(new WidgetPickerData()).when(mWidgetPickerDataProvider).get();
spyOn(mAppInfo);
SystemShortcut systemShortcut = SystemShortcut.WIDGETS
.getShortcut(mTestContext, mAppInfo, mView);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index c926ba9..1e2744c 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
import static com.android.launcher3.testing.shared.TestProtocol.WIDGET_CONFIG_NULL_EXTRA_INTENT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -64,7 +63,6 @@
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
@@ -237,16 +235,12 @@
}
protected void clearPackageData(String pkg) throws IOException, InterruptedException {
- final CountDownLatch count = new CountDownLatch(2);
- final SimpleBroadcastReceiver broadcastReceiver =
- new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, i -> count.countDown());
- // We OK to make binder calls on main thread in test.
- broadcastReceiver.registerPkgActions(mTargetContext, pkg,
- Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED);
-
- mDevice.executeShellCommand("pm clear " + pkg);
- assertTrue(pkg + " didn't restart", count.await(20, TimeUnit.SECONDS));
- mTargetContext.unregisterReceiver(broadcastReceiver);
+ assertTrue("pm clear command failed",
+ mDevice.executeShellCommand("pm clear " + pkg)
+ .contains("Success"));
+ assertTrue("pm wait-for-handler command failed",
+ mDevice.executeShellCommand("pm wait-for-handler")
+ .contains("Success"));
}
protected TestRule getRulesInsideActivityMonitor() {
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 27f6c16..567a8bd 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -45,6 +45,7 @@
* Common overview panel for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
+ private static final String TAG = "BaseOverview";
protected static final String TASK_RES_ID = "task";
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
@@ -384,39 +385,32 @@
}
protected boolean isActionsViewVisible() {
- boolean hasTasks = hasTasks();
- if (!hasTasks || isClearAllVisible()) {
- LauncherInstrumentation.log("Not expecting an actions bar:"
- + (!hasTasks ? "no recent tasks" : "clear all button is visible"));
+ if (!hasTasks() || isClearAllVisible()) {
+ Log.d(TAG, "Not expecting an actions bar: no tasks/'Clear all' is visible");
return false;
}
boolean isTablet = mLauncher.isTablet();
if (isTablet && mLauncher.isGridOnlyOverviewEnabled()) {
- LauncherInstrumentation.log("Not expecting an actions bar: "
- + "device is tablet with grid-only Overview");
+ Log.d(TAG, "Not expecting an actions bar: device is tablet with grid-only Overview");
return false;
}
OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask();
if (task == null) {
- LauncherInstrumentation.log("Not expecting an actions bar: no focused task");
+ Log.d(TAG, "Not expecting an actions bar: no current task");
return false;
}
- float centerOffset = Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX());
// In tablets, if focused task is not in center, overview actions aren't visible.
- if (isTablet && centerOffset >= 1) {
- LauncherInstrumentation.log("Not expecting an actions bar: "
- + "device is tablet and task is not centered; center offset by "
- + centerOffset + "px");
+ 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");
return false;
}
if (task.isTaskSplit() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
- LauncherInstrumentation.log("Not expecting an actions bar: "
- + "device is phone and task is split");
+ Log.d(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;
}
- LauncherInstrumentation.log("Expecting an actions bar");
+ Log.d(TAG, "Expecting an actions bar");
return true;
}
@@ -499,18 +493,13 @@
throw new IllegalStateException("Must be run on tablet device.");
}
final List<UiObject2> taskViews = getTasks();
- if (!hasTasks()) {
- LauncherInstrumentation.log("no recent tasks");
+ if (taskViews.size() == 0) {
return null;
}
int focusedTaskHeight = mLauncher.getFocusedTaskHeightForTablet();
for (UiObject2 task : taskViews) {
OverviewTask overviewTask = new OverviewTask(mLauncher, task, this);
- LauncherInstrumentation.log("checking task height ("
- + overviewTask.getVisibleHeight()
- + ") against defined focused task height ("
- + focusedTaskHeight + ")");
if (overviewTask.getVisibleHeight() == focusedTaskHeight) {
return overviewTask;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index ad37f7b..75c1b24 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1229,7 +1229,8 @@
void pressBackImpl() {
waitForLauncherInitialized();
final boolean launcherVisible =
- isTablet() ? isLauncherContainerVisible() : isLauncherVisible();
+ (isTablet() || isTaskbarNavbarUnificationEnabled()) ? isLauncherContainerVisible()
+ : isLauncherVisible();
boolean isThreeFingerTrackpadGesture =
mTrackpadGestureType == TrackpadGestureType.THREE_FINGER;
if (getNavigationModel() == NavigationModel.ZERO_BUTTON