Merge "Adding even more tracing for the tests" into main
diff --git a/Android.bp b/Android.bp
index 877f7bb..c4ea48a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,7 +75,7 @@
"androidx.test.uiautomator_uiautomator",
"androidx.preference_preference",
"SystemUISharedLib",
- "animationlib",
+ "//frameworks/libs/systemui:animationlib",
"launcher-testing-shared",
],
srcs: [
@@ -150,9 +150,9 @@
"androidx.cardview_cardview",
"androidx.window_window",
"com.google.android.material_material",
- "iconloader_base",
- "view_capture",
- "animationlib",
+ "//frameworks/libs/systemui:iconloader_base",
+ "//frameworks/libs/systemui:view_capture",
+ "//frameworks/libs/systemui:animationlib",
"SystemUI-statsd",
"launcher-testing-shared",
"androidx.lifecycle_lifecycle-common-java8",
diff --git a/OWNERS b/OWNERS
index efcf9f3..dd2d00e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -39,5 +39,9 @@
patmanning@google.com
helencheuk@google.com
+# Widget Picker team
+shamalip@google.com
+zakcohen@google.com
+
per-file FeatureFlags.java, globs = set noparent
per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index 5d489f5..d086da4 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -21,10 +21,9 @@
<LinearLayout
android:id="@+id/action_buttons"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/overview_actions_height"
- android:layout_gravity="bottom"
- android:gravity="center_horizontal"
+ android:layout_gravity="bottom|center_horizontal"
android:orientation="horizontal">
<Button
@@ -36,17 +35,12 @@
android:text="@string/action_screenshot"
android:theme="@style/ThemeControlHighlightWorkspaceColor" />
- <Space
- android:id="@+id/action_split_space"
- android:layout_width="@dimen/overview_actions_button_spacing"
- android:layout_height="1dp"
- android:visibility="gone" />
-
<Button
android:id="@+id/action_split"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/overview_actions_button_spacing"
android:text="@string/action_split"
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 9b68f49..048ff8b 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -95,12 +95,12 @@
<string name="action_share" msgid="2648470652637092375">"مشاركة"</string>
<string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string>
<string name="action_split" msgid="2098009717623550676">"تقسيم"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"حفظ إعدادات الميزة"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"حفظ استخدام التطبيقين معًا"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"انقر على تطبيق آخر لاستخدام وضع تقسيم الشاشة."</string>
- <string name="toast_contextual_split_select_app" msgid="433510957123687090">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\"."</string>
+ <string name="toast_contextual_split_select_app" msgid="433510957123687090">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\""</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"إلغاء"</b></string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"الخروج من وضع تقسيم الشاشة"</string>
- <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\"."</string>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"اختَر تطبيقًا آخر لاستخدام \"وضع تقسيم الشاشة\""</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"لا يسمح التطبيق أو لا تسمح مؤسستك بهذا الإجراء."</string>
<string name="split_widgets_not_supported" msgid="1355743038053053866">"التطبيقات المصغّرة غير متوفّرة حاليًا، يرجى اختيار تطبيق آخر."</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"هل تريد تخطي الدليل التوجيهي للتنقّل؟"</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index be66004..bd677da 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -95,7 +95,7 @@
<string name="action_share" msgid="2648470652637092375">"Споделяне"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string>
<string name="action_split" msgid="2098009717623550676">"Разделяне на екрана"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"Двойка прил.: Запис"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"Запис на двойка приложения"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Докоснете друго прил., за да ползвате разд. екран"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"За разделен екран изберете още едно приложение"</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Отказ"</b></string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 1cffbdf..5d6e0d8 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -129,7 +129,7 @@
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Es mostra la Barra de tasques"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"S\'ha amagat la Barra de tasques"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegació"</string>
- <string name="always_show_taskbar" msgid="3608801276107751229">"Mostra Barra de tasques"</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>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Separador de la Barra de tasques"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index f3f5777..5ebf7eb 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -112,7 +112,7 @@
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Přetáhněte aplikaci na stranu a používejte tak dvě najednou"</string>
<string name="taskbar_edu_stashing" msgid="5645461372669217294">"Panel aplikací zobrazíte pomalým přejetím prstem nahoru"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dostávejte návrhy aplikací podle toho, jaké používáte"</string>
- <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Dlouhým stisknutím oddělovače připnete panel aplikací"</string>
+ <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Dlouhým stisknutím oddělovače panel aplikací připnete"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"Více možností s panelem aplikací"</string>
<string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Stálé zobrazení panelu aplikací"</string>
<string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Pokud chcete, aby se panel aplikací vždy zobrazoval ve spodní části obrazovky, podržte oddělovač."</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 580a8df..5d5c2c1 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -129,7 +129,7 @@
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"Barra de navegación"</string>
- <string name="always_show_taskbar" msgid="3608801276107751229">"Ver siempre Barra de tareas"</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>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de la Barra de tareas"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 0900f78..79964d8 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -129,7 +129,7 @@
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Barra de tareas visible"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Barra de tareas oculta"</string>
<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="always_show_taskbar" msgid="3608801276107751229">"Barra de tareas visible"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Cambiar el modo de navegación"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Divisor de Barra de Tareas"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index e913f85..ca44b7a 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -109,9 +109,9 @@
<string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ohita"</string>
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Käännä näyttö"</string>
<string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Tehtäväpalkin ohje"</string>
- <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun, ja voit käyttää kahta sovellusta"</string>
+ <string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Vedä sovellus sivuun ja käytä kahta sovellusta"</string>
<string name="taskbar_edu_stashing" msgid="5645461372669217294">"Näytä tehtäväpalkki pyyhkäisemällä ylös hitaasti"</string>
- <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Sovellussuosituksia käytön perusteella"</string>
+ <string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Vastaanota sovellussuosituksia käytön perusteella"</string>
<string name="taskbar_edu_pinning" msgid="6708550858580071558">"Kiinnitä tehtäväpalkki painamalla jakajaa pitkään"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"Vinkkejä tehtäväpalkin tehokkaampaan käyttöön"</string>
<string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Näytä tehtäväpalkki aina"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 017c5f6..a61da68 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -129,7 +129,7 @@
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Feladatsáv megjelenítve"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Feladatsáv elrejtve"</string>
<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="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>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Feladatsáv-elválasztó"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 7fefe9c..64a8235 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -97,10 +97,10 @@
<string name="action_split" msgid="2098009717623550676">"Pisahkan"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"Simpan pasangan apl"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Ketuk aplikasi lain untuk memakai layar terpisah"</string>
- <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Pilih aplikasi lain untuk menggunakan layar terpisah"</string>
+ <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Pilih aplikasi lain untuk dibuka di layar terpisah"</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Batal"</b></string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Keluar dari pemilihan layar terpisah"</string>
- <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk memakai layar terpisah"</string>
+ <string name="toast_split_app_unsupported" msgid="2360229567007828914">"Pilih aplikasi lain untuk dibuka di layar terpisah"</string>
<string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
<string name="split_widgets_not_supported" msgid="1355743038053053866">"Widget saat ini tidak didukung, pilih aplikasi lain"</string>
<string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Lewati tutorial gestur?"</string>
@@ -113,7 +113,7 @@
<string name="taskbar_edu_stashing" msgid="5645461372669217294">"Geser perlahan ke atas untuk menampilkan Taskbar"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Dapatkan saran aplikasi berdasarkan rutinitas Anda"</string>
<string name="taskbar_edu_pinning" msgid="6708550858580071558">"Tekan lama pemisah untuk menyematkan Taskbar"</string>
- <string name="taskbar_edu_features" msgid="3320337287472848162">"Lakukan lebih banyak dengan Taskbar"</string>
+ <string name="taskbar_edu_features" msgid="3320337287472848162">"Lakukan lebih banyak hal dengan Taskbar"</string>
<string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Selalu tampilkan Taskbar"</string>
<string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Untuk selalu menampilkan Taskbar di bagian bawah layar Anda, sentuh & tahan pembatasnya"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Tutup"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 903534a..6dd4d52 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -112,7 +112,7 @@
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"ಒಂದೇ ಬಾರಿಗೆ 2 ಆ್ಯಪ್ಗಳನ್ನು ಬಳಸಲು ಆ್ಯಪ್ ಅನ್ನು ಬದಿಗೆ ಎಳೆಯಿರಿ"</string>
<string name="taskbar_edu_stashing" msgid="5645461372669217294">"ಟಾಸ್ಕ್ಬಾರ್ ಕಾಣುವಂತೆ ಮಾಡಲು ನಿಧಾನವಾಗಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"ನಿಮ್ಮ ದಿನಚರಿಯ ಆಧಾರದ ಮೇಲೆ ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
- <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ಟಾಸ್ಕ್ ಬಾರ್ ಅನ್ನು ಪಿನ್ ಮಾಡಲು ಡಿವೈಡರ್ ಮೇಲೆ ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string>
+ <string name="taskbar_edu_pinning" msgid="6708550858580071558">"ಟಾಸ್ಕ್ಬಾರ್ ಅನ್ನು ಪಿನ್ ಮಾಡಲು ಡಿವೈಡರ್ ಮೇಲೆ ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"ಟಾಸ್ಕ್ಬಾರ್ ಮೂಲಕ ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
<string name="taskbar_edu_pinning_title" msgid="210102174154211712">"ಯಾವಾಗಲೂ ಟಾಸ್ಕ್ಬಾರ್ ಅನ್ನು ತೋರಿಸಿ"</string>
<string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"ಯಾವಾಗಲೂ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಕೆಳಭಾಗದಲ್ಲಿ ಟಾಸ್ಕ್ ಬಾರ್ ಅನ್ನು ತೋರಿಸಲು, ಡಿವೈಡರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಿ"</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index b4c9569..4a3e62b 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -95,7 +95,7 @@
<string name="action_share" msgid="2648470652637092375">"Сподели"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string>
<string name="action_split" msgid="2098009717623550676">"Раздели"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"Зачувај пар аплик."</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"Зачувај го паров"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Допрете друга аплик. за да користите поделен екран"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Изберете друга апликација за да користите поделен екран"</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Откажи"</b></string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 762f669..06ed2c5 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -129,7 +129,7 @@
<string name="taskbar_a11y_shown_title" msgid="6842833581088937713">"Taskbar ပြထားသည်"</string>
<string name="taskbar_a11y_hidden_title" msgid="9154903639589659284">"Taskbar ဖျောက်ထားသည်"</string>
<string name="taskbar_phone_a11y_title" msgid="4933360237131229395">"လမ်းညွှန်ဘား"</string>
- <string name="always_show_taskbar" msgid="3608801276107751229">"Taskbar အမြဲပြပါ"</string>
+ <string name="always_show_taskbar" msgid="3608801276107751229">"Taskbar အမြဲပြရန်"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"ရွှေ့ကြည့်သည့်မုဒ် ပြောင်းရန်"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"လုပ်ဆောင်စရာဘား ပိုင်းခြားစနစ်"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 999da4e..74543a5 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -97,7 +97,7 @@
<string name="action_split" msgid="2098009717623550676">"Splitsen"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"App-paar opslaan"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Tik op nog een app om je scherm te splitsen"</string>
- <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Kies andere app om gesplitst scherm te gebruiken"</string>
+ <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Kies een andere app om gesplitst scherm te gebruiken"</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"Annuleren"</b></string>
<string name="toast_split_select_cont_desc" msgid="2119685056059607602">"Sluit de selectie voor gesplitst scherm"</string>
<string name="toast_split_app_unsupported" msgid="2360229567007828914">"Kies andere app om gesplitst scherm te gebruiken"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 3f6cc2c..fea79bb 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -95,7 +95,7 @@
<string name="action_share" msgid="2648470652637092375">"分享"</string>
<string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
<string name="action_split" msgid="2098009717623550676">"拆分"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"保存应用对"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"保存应用组合"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"另外选择一个应用才可使用分屏模式"</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string>
@@ -110,7 +110,7 @@
<string name="accessibility_rotate_button" msgid="4771825231336502943">"旋转屏幕"</string>
<string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"任务栏教程"</string>
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"将一个应用拖到一侧,即可同时使用两个应用"</string>
- <string name="taskbar_edu_stashing" msgid="5645461372669217294">"缓慢向上滑动即可显示任务栏"</string>
+ <string name="taskbar_edu_stashing" msgid="5645461372669217294">"缓慢上滑即可显示任务栏"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"根据您的日常使用习惯获得应用建议"</string>
<string name="taskbar_edu_pinning" msgid="6708550858580071558">"长按分隔线即可固定任务栏"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"体验任务栏的更多功能"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 050c47c..c1c78ab 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -95,7 +95,7 @@
<string name="action_share" msgid="2648470652637092375">"分享"</string>
<string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
<string name="action_split" msgid="2098009717623550676">"分割"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"儲存應用程式配對"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"儲存應用程式組合"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"輕按其他應用程式以使用分割螢幕"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"選擇其他應用程式才能使用分割螢幕"</string>
<string name="toast_split_select_app_cancel" msgid="1532690483356445639"><b>"取消"</b></string>
diff --git a/quickstep/src/com/android/launcher3/HomeTransitionController.java b/quickstep/src/com/android/launcher3/HomeTransitionController.java
deleted file mode 100644
index c4a2e9e..0000000
--- a/quickstep/src/com/android/launcher3/HomeTransitionController.java
+++ /dev/null
@@ -1,55 +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;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.SystemUiProxy;
-import com.android.wm.shell.shared.IHomeTransitionListener;
-
-/**
- * Controls launcher response to home activity visibility changing.
- */
-public class HomeTransitionController {
-
- @Nullable private QuickstepLauncher mLauncher;
- @Nullable private IHomeTransitionListener mHomeTransitionListener;
-
- public void registerHomeTransitionListener(QuickstepLauncher launcher) {
- mLauncher = launcher;
- mHomeTransitionListener = new IHomeTransitionListener.Stub() {
- @Override
- public void onHomeVisibilityChanged(boolean isVisible) {
- MAIN_EXECUTOR.execute(() -> {
- if (mLauncher != null && mLauncher.getTaskbarUIController() != null) {
- mLauncher.getTaskbarUIController().onLauncherVisibilityChanged(isVisible);
- }
- });
- }
- };
-
- SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(mHomeTransitionListener);
- }
-
- public void unregisterHomeTransitionListener() {
- SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(null);
- mHomeTransitionListener = null;
- mLauncher = null;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 8c4db4a..23cb8e9 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -35,23 +35,31 @@
import android.view.WindowManager;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.dragndrop.SimpleDragLayer;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.WidgetPredictionsRequester;
import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.BaseWidgetSheet;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/** An Activity that can host Launcher's widget picker. */
public class WidgetPickerActivity extends BaseActivity {
private static final String TAG = "WidgetPickerActivity";
-
/**
* Name of the extra that indicates that a widget being dragged.
*
@@ -64,14 +72,33 @@
// the intent, then widgets will not be filtered for size.
private static final String EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width";
private static final String EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height";
-
+ /**
+ * Widgets currently added by the user in the UI surface.
+ * <p>This allows widget picker to exclude existing widgets from suggestions.</p>
+ */
+ private static final String EXTRA_ADDED_APP_WIDGETS = "added_app_widgets";
+ /**
+ * A unique identifier of the surface hosting the widgets;
+ * <p>"widgets" is reserved for home screen surface.</p>
+ * <p>"widgets_hub" is reserved for glanceable hub surface.</p>
+ */
+ private static final String EXTRA_UI_SURFACE = "ui_surface";
+ private static final Pattern UI_SURFACE_PATTERN =
+ Pattern.compile("^(widgets|widgets_hub)$");
private SimpleDragLayer<WidgetPickerActivity> mDragLayer;
private WidgetsModel mModel;
+ private LauncherAppState mApp;
+ private WidgetPredictionsRequester mWidgetPredictionsRequester;
private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
private int mDesiredWidgetWidth;
private int mDesiredWidgetHeight;
private int mWidgetCategoryFilter;
+ @Nullable
+ private String mUiSurface;
+ // Widgets existing on the host surface.
+ @NonNull
+ private List<AppWidgetProviderInfo> mAddedWidgets = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -80,9 +107,8 @@
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
- LauncherAppState app = LauncherAppState.getInstance(this);
- InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
-
+ mApp = LauncherAppState.getInstance(this);
+ InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
mDeviceProfile = idp.getDeviceProfile(this);
mModel = new WidgetsModel();
@@ -97,6 +123,11 @@
widgetSheet.disableNavBarScrim(true);
widgetSheet.addOnCloseListener(this::finish);
+ parseIntentExtras();
+ refreshAndBindWidgets();
+ }
+
+ private void parseIntentExtras() {
// A value of 0 for either size means that no filtering will occur in that dimension. If
// both values are 0, then no size filtering will occur.
mDesiredWidgetWidth =
@@ -108,7 +139,15 @@
mWidgetCategoryFilter =
getIntent().getIntExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, 0);
- refreshAndBindWidgets();
+ String uiSurfaceParam = getIntent().getStringExtra(EXTRA_UI_SURFACE);
+ if (uiSurfaceParam != null && UI_SURFACE_PATTERN.matcher(uiSurfaceParam).matches()) {
+ mUiSurface = uiSurfaceParam;
+ }
+ ArrayList<AppWidgetProviderInfo> addedWidgets = getIntent().getParcelableArrayListExtra(
+ EXTRA_ADDED_APP_WIDGETS, AppWidgetProviderInfo.class);
+ if (addedWidgets != null) {
+ mAddedWidgets = addedWidgets;
+ }
}
@NonNull
@@ -179,11 +218,12 @@
};
}
+ /** Updates the model with widgets and provides them after applying the provided filter. */
private void refreshAndBindWidgets() {
MODEL_EXECUTOR.execute(() -> {
LauncherAppState app = LauncherAppState.getInstance(this);
mModel.update(app, null);
- final ArrayList<WidgetsListBaseEntry> widgets =
+ final List<WidgetsListBaseEntry> allWidgets =
mModel.getFilteredWidgetsListForPicker(
app.getContext(),
/*widgetItemFilter=*/ widget -> {
@@ -193,10 +233,37 @@
return verdict.isAcceptable;
}
);
- MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
+ bindWidgets(allWidgets);
+ if (mUiSurface != null) {
+ Map<PackageUserKey, List<WidgetItem>> allWidgetsMap = allWidgets.stream()
+ .filter(WidgetsListHeaderEntry.class::isInstance)
+ .collect(Collectors.toMap(
+ entry -> PackageUserKey.fromPackageItemInfo(entry.mPkgItem),
+ entry -> entry.mWidgets)
+ );
+ mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(),
+ mUiSurface, allWidgetsMap);
+ mWidgetPredictionsRequester.request(mAddedWidgets, this::bindRecommendedWidgets);
+ }
});
}
+ private void bindWidgets(List<WidgetsListBaseEntry> widgets) {
+ MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
+ }
+
+ private void bindRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
+ MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setRecommendedWidgets(recommendedWidgets));
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mWidgetPredictionsRequester != null) {
+ mWidgetPredictionsRequester.clear();
+ }
+ }
+
private WidgetAcceptabilityVerdict isWidgetAcceptable(WidgetItem widget) {
final AppWidgetProviderInfo info = widget.widgetInfo;
if (info == null) {
diff --git a/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
new file mode 100644
index 0000000..8431396
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WidgetPredictionsRequester.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+
+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.picker.WidgetRecommendationCategoryProvider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * Works with app predictor to fetch and process widget predictions displayed in a standalone
+ * widget picker activity for a UI surface.
+ */
+public class WidgetPredictionsRequester {
+ private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
+ private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
+
+ @Nullable
+ private AppPredictor mAppPredictor;
+ private final Context mContext;
+ @NonNull
+ private final String mUiSurface;
+ @NonNull
+ private final Map<PackageUserKey, List<WidgetItem>> mAllWidgets;
+
+ public WidgetPredictionsRequester(Context context, @NonNull String uiSurface,
+ @NonNull Map<PackageUserKey, List<WidgetItem>> allWidgets) {
+ mContext = context;
+ mUiSurface = uiSurface;
+ mAllWidgets = Collections.unmodifiableMap(allWidgets);
+ }
+
+ /**
+ * Requests predictions from the app predictions manager and registers the provided callback to
+ * receive updates when predictions are available.
+ *
+ * @param existingWidgets widgets that are currently added to the surface;
+ * @param callback consumer of prediction results to be called when predictions are
+ * available
+ */
+ public void request(List<AppWidgetProviderInfo> existingWidgets,
+ Consumer<List<ItemInfo>> callback) {
+ Bundle bundle = buildBundleForPredictionSession(existingWidgets, mUiSurface);
+ Predicate<WidgetItem> filter = notOnUiSurfaceFilter(existingWidgets);
+
+ MODEL_EXECUTOR.execute(() -> {
+ clear();
+ AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
+ if (apm == null) {
+ return;
+ }
+
+ mAppPredictor = apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(mContext)
+ .setUiSurface(mUiSurface)
+ .setExtras(bundle)
+ .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
+ .build());
+ mAppPredictor.registerPredictionUpdates(MODEL_EXECUTOR,
+ targets -> bindPredictions(targets, filter, callback));
+ mAppPredictor.requestPredictionUpdate();
+ });
+ }
+
+ /**
+ * Returns a bundle that can be passed in a prediction session
+ *
+ * @param addedWidgets widgets that are already added by the user in the ui surface
+ * @param uiSurface a unique identifier of the surface hosting widgets; format
+ * "widgets_xx"; note - "widgets" is reserved for home screen surface.
+ */
+ @VisibleForTesting
+ static Bundle buildBundleForPredictionSession(List<AppWidgetProviderInfo> addedWidgets,
+ String uiSurface) {
+ Bundle bundle = new Bundle();
+ ArrayList<AppTargetEvent> addedAppTargetEvents = new ArrayList<>();
+ for (AppWidgetProviderInfo info : addedWidgets) {
+ ComponentName componentName = info.provider;
+ AppTargetEvent appTargetEvent = buildAppTargetEvent(uiSurface, info, componentName);
+ addedAppTargetEvents.add(appTargetEvent);
+ }
+ bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, addedAppTargetEvents);
+ return bundle;
+ }
+
+ /**
+ * Builds the AppTargetEvent for added widgets in a form that can be passed to the widget
+ * predictor.
+ * Also see {@link PredictionHelper}
+ */
+ private static AppTargetEvent buildAppTargetEvent(String uiSurface, AppWidgetProviderInfo info,
+ ComponentName componentName) {
+ AppTargetId appTargetId = new AppTargetId("widget:" + componentName.getPackageName());
+ AppTarget appTarget = new AppTarget.Builder(appTargetId, componentName.getPackageName(),
+ /*user=*/ info.getProfile()).setClassName(componentName.getClassName()).build();
+ return new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_PIN)
+ .setLaunchLocation(uiSurface).build();
+ }
+
+ /**
+ * Returns a filter to match {@link WidgetItem}s that don't exist on the UI surface.
+ */
+ @NonNull
+ @VisibleForTesting
+ static Predicate<WidgetItem> notOnUiSurfaceFilter(
+ List<AppWidgetProviderInfo> existingWidgets) {
+ Set<ComponentKey> existingComponentKeys = existingWidgets.stream().map(
+ widget -> new ComponentKey(widget.provider, widget.getProfile())).collect(
+ Collectors.toSet());
+ return widgetItem -> !existingComponentKeys.contains(widgetItem);
+ }
+
+ /** Provides the predictions returned by the predictor to the registered callback. */
+ @WorkerThread
+ private void bindPredictions(List<AppTarget> targets, Predicate<WidgetItem> filter,
+ Consumer<List<ItemInfo>> callback) {
+ List<WidgetItem> filteredPredictions = filterPredictions(targets, mAllWidgets, filter);
+ List<ItemInfo> mappedPredictions = mapWidgetItemsToItemInfo(filteredPredictions);
+
+ MAIN_EXECUTOR.execute(() -> callback.accept(mappedPredictions));
+ }
+
+ /**
+ * Applies the provided filter (e.g. widgets not on workspace) on the predictions returned by
+ * the predictor.
+ */
+ @VisibleForTesting
+ static List<WidgetItem> filterPredictions(List<AppTarget> predictions,
+ Map<PackageUserKey, List<WidgetItem>> allWidgets, Predicate<WidgetItem> filter) {
+ List<WidgetItem> servicePredictedItems = new ArrayList<>();
+ List<WidgetItem> localFilteredWidgets = new ArrayList<>();
+
+ for (AppTarget prediction : predictions) {
+ List<WidgetItem> widgetsInPackage = allWidgets.get(
+ new PackageUserKey(prediction.getPackageName(), prediction.getUser()));
+ if (widgetsInPackage == null || widgetsInPackage.isEmpty()) {
+ continue;
+ }
+ String className = prediction.getClassName();
+ if (!TextUtils.isEmpty(className)) {
+ WidgetItem item = widgetsInPackage.stream()
+ .filter(w -> className.equals(w.componentName.getClassName()))
+ .filter(filter)
+ .findFirst().orElse(null);
+ if (item != null) {
+ servicePredictedItems.add(item);
+ continue;
+ }
+ }
+ // No widget was added by the service, try local filtering
+ widgetsInPackage.stream().filter(filter).findFirst()
+ .ifPresent(localFilteredWidgets::add);
+ }
+ if (servicePredictedItems.isEmpty()) {
+ servicePredictedItems.addAll(localFilteredWidgets);
+ }
+
+ return servicePredictedItems;
+ }
+
+ /**
+ * Converts the list of {@link WidgetItem}s to the list of {@link ItemInfo}s.
+ */
+ private List<ItemInfo> mapWidgetItemsToItemInfo(List<WidgetItem> widgetItems) {
+ List<ItemInfo> items;
+ if (enableCategorizedWidgetSuggestions()) {
+ WidgetRecommendationCategoryProvider categoryProvider =
+ WidgetRecommendationCategoryProvider.newInstance(mContext);
+ items = widgetItems.stream()
+ .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION,
+ categoryProvider.getWidgetRecommendationCategory(mContext, it)))
+ .collect(Collectors.toList());
+ } else {
+ items = widgetItems.stream().map(it -> new PendingAddWidgetInfo(it.widgetInfo,
+ CONTAINER_WIDGETS_PREDICTION)).collect(Collectors.toList());
+ }
+ return items;
+ }
+
+ /** Cleans up any open prediction sessions. */
+ public void clear() {
+ if (mAppPredictor != null) {
+ mAppPredictor.destroy();
+ mAppPredictor = null;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index f9a8c99..176091a 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.os.Debug;
import android.os.SystemProperties;
@@ -136,9 +135,6 @@
Log.d(TAG, "setVisibleFreeformTasksCount: visibleTasksCount=" + visibleTasksCount
+ " currentValue=" + mVisibleFreeformTasksCount);
}
- if (!enableDesktopWindowingMode()) {
- return;
- }
if (visibleTasksCount != mVisibleFreeformTasksCount) {
final boolean wasVisible = mVisibleFreeformTasksCount > 0;
@@ -180,9 +176,6 @@
Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled
+ " currentValue=" + mInOverviewState);
}
- if (!enableDesktopWindowingMode()) {
- return;
- }
if (overviewStateEnabled != mInOverviewState) {
mInOverviewState = overviewStateEnabled;
if (mInOverviewState) {
@@ -202,9 +195,6 @@
Log.d(TAG, "setBackgroundStateEnabled: enabled=" + backgroundStateEnabled
+ " currentValue=" + mBackgroundStateEnabled);
}
- if (!enableDesktopWindowingMode()) {
- return;
- }
if (backgroundStateEnabled != mBackgroundStateEnabled) {
mBackgroundStateEnabled = backgroundStateEnabled;
if (mBackgroundStateEnabled) {
@@ -229,9 +219,6 @@
* Notify controller that recents gesture has started.
*/
public void setRecentsGestureStart() {
- if (!enableDesktopWindowingMode()) {
- return;
- }
if (DEBUG) {
Log.d(TAG, "setRecentsGestureStart");
}
@@ -243,9 +230,6 @@
* {@link com.android.quickstep.GestureState.GestureEndTarget}
*/
public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) {
- if (!enableDesktopWindowingMode()) {
- return;
- }
if (DEBUG) {
Log.d(TAG, "setRecentsGestureEnd: endTarget=" + endTarget);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index bed85d7..d89f49b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
-
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@@ -117,9 +115,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- enableDesktopWindowingMode()
- && desktopController != null
- && desktopController.areFreeformTasksVisible();
+ desktopController != null && desktopController.areFreeformTasksVisible();
if (mModel.isTaskListValid(mTaskListChangeId)) {
// When we are opening the KQS with no focus override, check if the first task is
@@ -158,14 +154,12 @@
// Hide all desktop tasks and show them on the hidden tile
int hiddenDesktopTasks = 0;
- if (enableDesktopWindowingMode()) {
- DesktopTask desktopTask = findDesktopTask(tasks);
- if (desktopTask != null) {
- hiddenDesktopTasks = desktopTask.tasks.size();
- tasks = tasks.stream()
- .filter(t -> !(t instanceof DesktopTask))
- .collect(Collectors.toCollection(ArrayList<GroupTask>::new));
- }
+ DesktopTask desktopTask = findDesktopTask(tasks);
+ if (desktopTask != null) {
+ hiddenDesktopTasks = desktopTask.tasks.size();
+ tasks = tasks.stream()
+ .filter(t -> !(t instanceof DesktopTask))
+ .collect(Collectors.toCollection(ArrayList<GroupTask>::new));
}
mTasks = tasks.stream()
.limit(MAX_TASKS)
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index a59aead..111b397 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -35,6 +34,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
@@ -49,8 +49,10 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.HomeVisibilityState;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
@@ -79,6 +81,7 @@
AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
private final QuickstepLauncher mLauncher;
+ private final HomeVisibilityState mHomeState;
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
dp -> {
@@ -87,6 +90,8 @@
mControllers.taskbarViewController.onRotationChanged(dp);
}
};
+ private final HomeVisibilityState.VisibilityChangeListener mVisibilityChangeListener =
+ this::onLauncherVisibilityChanged;
// Initialized in init.
private final TaskbarLauncherStateController
@@ -94,6 +99,7 @@
public LauncherTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
+ mHomeState = SystemUiProxy.INSTANCE.get(mLauncher).getHomeVisibilityState();
}
@Override
@@ -104,8 +110,11 @@
mControllers.getSharedState().sysuiStateFlags);
mLauncher.setTaskbarUIController(this);
-
- onLauncherVisibilityChanged(mLauncher.hasBeenResumed(), true /* fromInit */);
+ mHomeState.addListener(mVisibilityChangeListener);
+ onLauncherVisibilityChanged(
+ Flags.useActivityOverlay()
+ ? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed(),
+ true /* fromInit */);
onStashedInAppChanged(mLauncher.getDeviceProfile());
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
@@ -129,6 +138,7 @@
mLauncher.setTaskbarUIController(null);
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+ mHomeState.removeListener(mVisibilityChangeListener);
updateTaskTransitionSpec(true);
}
@@ -209,9 +219,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- enableDesktopWindowingMode()
- && desktopController != null
- && desktopController.areFreeformTasksVisible();
+ desktopController != null && desktopController.areFreeformTasksVisible();
if (onDesktop) {
isVisible = false;
}
@@ -234,7 +242,8 @@
@Override
public void refreshResumedState() {
- onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
+ onLauncherVisibilityChanged(Flags.useActivityOverlay()
+ ? mHomeState.isHomeVisible() : mLauncher.hasBeenResumed());
}
@Override
@@ -312,6 +321,11 @@
mControllers.taskbarEduTooltipController.maybeShowSwipeEdu();
}
+ /** Will make the next onRecentsAnimationFinished() animation a no-op. */
+ public void setSkipNextRecentsAnimEnd() {
+ mTaskbarLauncherStateController.setSkipNextRecentsAnimEnd();
+ }
+
/**
* Returns {@code true} if a Taskbar education should be shown on application launch.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8769f11..390dec9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -1233,13 +1233,13 @@
return;
}
- boolean findExactPairMatch = itemInfos.size() == 2;
+ boolean isLaunchingAppPair = itemInfos.size() == 2;
// Convert the list of ItemInfo instances to a list of ComponentKeys
List<ComponentKey> componentKeys =
itemInfos.stream().map(ItemInfo::getComponentKey).toList();
recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
componentKeys,
- findExactPairMatch,
+ isLaunchingAppPair,
foundTasks -> {
@Nullable Task foundTask = foundTasks[0];
if (foundTask != null) {
@@ -1253,10 +1253,18 @@
}
}
- if (findExactPairMatch) {
- // We did not find the app pair we were looking for, so launch one.
- recents.getSplitSelectController().getAppPairsController().launchAppPair(
- (AppPairIcon) launchingIconView, -1 /*cuj*/);
+ if (isLaunchingAppPair) {
+ // Finish recents animation if it's running before launching to ensure
+ // we get both leashes for the animation
+ mControllers.uiController.setSkipNextRecentsAnimEnd();
+ recents.switchToScreenshot(() ->
+ recents.finishRecentsAnimation(true /*toRecents*/,
+ false /*shouldPip*/,
+ () -> recents
+ .getSplitSelectController()
+ .getAppPairsController()
+ .launchAppPair((AppPairIcon) launchingIconView,
+ -1 /*cuj*/)));
} else {
startItemInfoActivity(itemInfos.get(0), foundTask);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 8d48154..b0abbe9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -148,6 +148,7 @@
private Integer mPrevState;
private int mState;
private LauncherState mLauncherState = LauncherState.NORMAL;
+ private boolean mSkipNextRecentsAnimEnd;
// Time when FLAG_TASKBAR_HIDDEN was last cleared, SystemClock.elapsedRealtime (milliseconds).
private long mLastUnlockTimeMs = 0;
@@ -292,12 +293,12 @@
if (mTaskBarRecentsAnimationListener != null) {
mTaskBarRecentsAnimationListener.endGestureStateOverride(
- !mLauncher.isInState(LauncherState.OVERVIEW));
+ !mLauncher.isInState(LauncherState.OVERVIEW), false /*canceled*/);
}
mTaskBarRecentsAnimationListener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(mTaskBarRecentsAnimationListener);
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() ->
- mTaskBarRecentsAnimationListener.endGestureStateOverride(true));
+ mTaskBarRecentsAnimationListener.endGestureStateOverride(true, false /*canceled*/));
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchCancelledRunnable(() -> {
updateStateForUserFinishedToApp(false /* finishedToApp */);
@@ -318,6 +319,11 @@
mShouldDelayLauncherStateAnim = shouldDelayLauncherStateAnim;
}
+ /** Will make the next onRecentsAnimationFinished() a no-op. */
+ public void setSkipNextRecentsAnimEnd() {
+ mSkipNextRecentsAnimEnd = true;
+ }
+
/** SysUI flags updated, see QuickStepContract.SYSUI_STATE_* values. */
public void updateStateForSysuiFlags(int systemUiStateFlags) {
updateStateForSysuiFlags(systemUiStateFlags, /* applyState */ true);
@@ -656,7 +662,8 @@
* Returns if the current Launcher state has hotseat on top of other elemnets.
*/
public boolean isInHotseatOnTopStates() {
- return mLauncherState != LauncherState.ALL_APPS;
+ return mLauncherState != LauncherState.ALL_APPS
+ && !mLauncher.getWorkspace().isOverlayShown();
}
boolean isInOverview() {
@@ -770,19 +777,33 @@
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
boolean isInOverview = mLauncher.isInState(LauncherState.OVERVIEW);
- endGestureStateOverride(!isInOverview);
+ endGestureStateOverride(!isInOverview, true /*canceled*/);
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- endGestureStateOverride(!controller.getFinishTargetIsLauncher());
+ endGestureStateOverride(!controller.getFinishTargetIsLauncher(), false /*canceled*/);
}
- private void endGestureStateOverride(boolean finishedToApp) {
+ /**
+ * Handles whatever cleanup is needed after the recents animation is completed.
+ * NOTE: If {@link #mSkipNextRecentsAnimEnd} is set and we're coming from a non-cancelled
+ * path, this will not call {@link #updateStateForUserFinishedToApp(boolean)}
+ *
+ * @param finishedToApp {@code true} if the recents animation finished to showing an app and
+ * not workspace or overview
+ * @param canceled {@code true} if the recents animation was canceled instead of finishing
+ * to completion
+ */
+ private void endGestureStateOverride(boolean finishedToApp, boolean canceled) {
mCallbacks.removeListener(this);
mTaskBarRecentsAnimationListener = null;
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
+ if (mSkipNextRecentsAnimEnd && !canceled) {
+ mSkipNextRecentsAnimEnd = false;
+ return;
+ }
updateStateForUserFinishedToApp(finishedToApp);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index e293ad4..03f55ca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -27,7 +27,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -281,12 +280,10 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
- if (enableDesktopWindowingMode()) {
- DesktopVisibilityController desktopVisibilityController =
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- desktopVisibilityController.onHomeActionTriggered();
- }
+ DesktopVisibilityController desktopVisibilityController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.onHomeActionTriggered();
}
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 4462f20..2730be1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -118,7 +118,7 @@
FolderInfo fi = (FolderInfo) info;
if (fi.anyMatch(matcher)) {
FolderDotInfo folderDotInfo = new FolderDotInfo();
- for (WorkspaceItemInfo si : fi.getContents()) {
+ for (ItemInfo si : fi.getContents()) {
folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si));
}
((FolderIcon) v).setDotInfo(folderDotInfo);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 3d58464..7e74c27 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -598,7 +598,8 @@
? stashTranslation : 0)
.setDuration(duration));
mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
- hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
+ (hasAnyFlag(FLAG_STASHED_IN_APP_IME) && isStashed) ? 0 : 1).setDuration(
+ duration));
mAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
mAnimator = null;
mIsStashed = isStashed;
@@ -890,17 +891,11 @@
}
/**
- * Should be called when a system gesture starts and settles, so we can defer updating
- * FLAG_STASHED_IN_APP_IME until after the gesture transition completes.
+ * Should be called when a system gesture starts and settles, so we can remove
+ * FLAG_STASHED_IN_APP_IME while the gesture is in progress.
*/
public void setSystemGestureInProgress(boolean inProgress) {
mIsSystemGestureInProgress = inProgress;
- if (mIsSystemGestureInProgress) {
- return;
- }
-
- // Only update the following flags when system gesture is not in progress.
- updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
setStashedImeState();
}
@@ -952,12 +947,9 @@
&& !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY);
updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked);
- // Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING);
-
- if (!mIsSystemGestureInProgress) {
- updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme());
+ if (updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme())) {
animDuration = TASKBAR_STASH_DURATION_FOR_IME;
startDelay = getTaskbarStashStartDelayForIme();
}
@@ -970,7 +962,8 @@
*
* <p>Do not stash if in small screen, with 3 button nav, and in landscape (or seascape).
* <p>Do not stash if taskbar is transient.
- * <p>Do not stash if hardware keyboard is attached and taskbar is pinned and IME is docked
+ * <p>Do not stash if hardware keyboard is attached and taskbar is pinned and IME is docked.
+ * <p>Do not stash if a system gesture is started.
*/
private boolean shouldStashForIme() {
if (DisplayController.isTransientTaskbar(mActivity)) {
@@ -996,6 +989,11 @@
return false;
}
+ // Do not stash if a gesture started.
+ if (mIsSystemGestureInProgress) {
+ return false;
+ }
+
return mIsImeShowing || mIsImeSwitcherShowing;
}
@@ -1007,13 +1005,16 @@
* @param flag The flag to update.
* @param enabled Whether to enable the flag: True will cause the task bar to be stashed /
* unstashed.
+ * @return Whether the flag state changed.
*/
- public void updateStateForFlag(int flag, boolean enabled) {
+ public boolean updateStateForFlag(int flag, boolean enabled) {
+ int oldState = mState;
if (enabled) {
mState |= flag;
} else {
mState &= ~flag;
}
+ return mState != oldState;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 109400e..cb0fa40 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -399,4 +399,12 @@
mControllers.taskbarStashController.updateStateForFlag(FLAG_IN_APP, !isVisible);
mControllers.taskbarStashController.applyState();
}
+
+ /**
+ * Request for UI controller to ignore animations for the next callback for the end of recents
+ * animation
+ */
+ public void setSkipNextRecentsAnimEnd() {
+ // Overridden
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index dc2684e..effef3c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -134,7 +135,7 @@
int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
int actualIconSize = mActivityContext.getDeviceProfile().taskbarIconSize;
- if (enableTaskbarPinning()) {
+ if (enableTaskbarPinning() && !mActivityContext.isThreeButtonNav()) {
DeviceProfile deviceProfile = mActivityContext.getTransientTaskbarDeviceProfile();
actualIconSize = deviceProfile.taskbarIconSize;
}
@@ -472,6 +473,29 @@
iconEnd = centerAlignIconEnd + offset;
}
+ // Currently, we support only one device with display cutout and we only are concern about
+ // it when the bottom rect is present and non empty
+ DisplayCutout displayCutout = getDisplay().getCutout();
+ if (displayCutout != null && !displayCutout.getBoundingRectBottom().isEmpty()) {
+ Rect cutoutBottomRect = displayCutout.getBoundingRectBottom();
+ // when cutout present at the bottom of screen align taskbar icons to cutout offset
+ // if taskbar icon overlaps with cutout
+ int taskbarIconLeftBound = iconEnd - spaceNeeded;
+ int taskbarIconRightBound = iconEnd;
+
+ boolean doesTaskbarIconsOverlapWithCutout =
+ taskbarIconLeftBound <= cutoutBottomRect.centerX()
+ && cutoutBottomRect.centerX() <= taskbarIconRightBound;
+
+ if (doesTaskbarIconsOverlapWithCutout) {
+ if (!layoutRtl) {
+ iconEnd = spaceNeeded + cutoutBottomRect.width();
+ } else {
+ iconEnd = right - cutoutBottomRect.width();
+ }
+ }
+ }
+
sTmpRect.set(mIconLayoutBounds);
// Layout the children
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 1f7f0a7..5d0eac3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -337,11 +337,6 @@
private void updateTaskbarIconTranslationXForPinning() {
View[] iconViews = mTaskbarView.getIconViews();
float scale = mTaskbarIconTranslationXForPinning.value;
- float taskbarCenterX =
- mTaskbarView.getLeft() + (mTaskbarView.getRight() - mTaskbarView.getLeft()) / 2.0f;
-
- float finalMarginScale = mapRange(scale, 0f, mTransientIconSize - mPersistentIconSize);
-
float transientTaskbarAllAppsOffset = mActivity.getResources().getDimension(
mTaskbarView.getAllAppsButtonTranslationXOffset(true));
float persistentTaskbarAllAppsOffset = mActivity.getResources().getDimension(
@@ -354,6 +349,17 @@
allAppIconTranslateRange *= -1;
}
+ if (mActivity.isThreeButtonNav()) {
+ ((IconButtonView) mTaskbarView.getAllAppsButtonView())
+ .setTranslationXForTaskbarAllAppsIcon(allAppIconTranslateRange);
+ return;
+ }
+
+ float taskbarCenterX =
+ mTaskbarView.getLeft() + (mTaskbarView.getRight() - mTaskbarView.getLeft()) / 2.0f;
+
+ float finalMarginScale = mapRange(scale, 0f, mTransientIconSize - mPersistentIconSize);
+
float halfIconCount = iconViews.length / 2.0f;
for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
View iconView = iconViews[iconIndex];
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 3b04602..99937f8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -207,10 +207,10 @@
}
@Override
- protected void onScaleProgressChanged() {
- super.onScaleProgressChanged();
- mAppsView.setClipChildren(!mIsBackProgressing);
- mAppsView.getAppsRecyclerViewContainer().setClipChildren(!mIsBackProgressing);
+ protected void onUserSwipeToDismissProgressChanged() {
+ super.onUserSwipeToDismissProgressChanged();
+ mAppsView.setClipChildren(!mIsDismissInProgress);
+ mAppsView.getAppsRecyclerViewContainer().setClipChildren(!mIsDismissInProgress);
}
@Override
@@ -264,7 +264,7 @@
if (mAllAppsCallbacks.handleSearchBackInvoked()) {
// We need to scale back taskbar all apps if we navigate back within search inside all
// apps
- animateSlideInViewToNoScale();
+ animateSwipeToDismissProgressToStart();
} else {
super.onBackInvoked();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 1f3c483..bae30e3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -151,6 +151,9 @@
private BubbleStashController mBubbleStashController;
private BubbleStashedHandleViewController mBubbleStashedHandleViewController;
+ // Keep track of bubble bar bounds sent to shell to avoid sending duplicate updates
+ private final Rect mLastSentBubbleBarBounds = new Rect();
+
/**
* Similar to {@link BubbleBarUpdate} but rather than {@link BubbleInfo}s it uses
* {@link BubbleBarBubble}s so that it can be used to update the views.
@@ -220,7 +223,8 @@
mBubbleStashedHandleViewController.setHiddenForBubbles(
!sBubbleBarEnabled || mBubbles.isEmpty());
mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
- key -> setSelectedBubble(mBubbles.get(key)));
+ key -> setSelectedBubbleInternal(mBubbles.get(key)));
+ mBubbleBarViewController.setBoundsChangeListener(this::onBubbleBarBoundsChanged);
});
}
@@ -390,7 +394,7 @@
}
}
if (bubbleToSelect != null) {
- setSelectedBubble(bubbleToSelect);
+ setSelectedBubbleInternal(bubbleToSelect);
if (previouslySelectedBubble == null) {
mBubbleStashController.animateToInitialState(update.expanded);
}
@@ -409,8 +413,7 @@
if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
// Animate when receiving updates. Skip it if we received the initial state.
boolean animate = !update.initialState;
- mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate);
- mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation);
+ updateBubbleBarLocationInternal(update.bubbleBarLocation, animate);
}
}
}
@@ -427,7 +430,9 @@
info.getFlags() | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
mSelectedBubble.getView().updateDotVisibility(true /* animate */);
}
- mSystemUiProxy.showBubble(getSelectedBubbleKey(), getExpandedBubbleBarDisplayBounds());
+ Rect bounds = getExpandedBubbleBarDisplayBounds();
+ mLastSentBubbleBarBounds.set(bounds);
+ mSystemUiProxy.showBubble(getSelectedBubbleKey(), bounds);
} else {
Log.w(TAG, "Trying to show the selected bubble but it's null");
}
@@ -436,7 +441,7 @@
/** Updates the currently selected bubble for launcher views and tells WMShell to show it. */
public void showAndSelectBubble(BubbleBarItem b) {
if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey());
- setSelectedBubble(b);
+ setSelectedBubbleInternal(b);
showSelectedBubble();
}
@@ -445,7 +450,7 @@
* WMShell that the selection has changed, that should go through either
* {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}.
*/
- private void setSelectedBubble(BubbleBarItem b) {
+ private void setSelectedBubbleInternal(BubbleBarItem b) {
if (!Objects.equals(b, mSelectedBubble)) {
if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
mSelectedBubble = b;
@@ -464,6 +469,21 @@
return null;
}
+ /**
+ * Set a new bubble bar location.
+ * <p>
+ * Updates the value locally in Launcher and in WMShell.
+ */
+ public void updateBubbleBarLocation(BubbleBarLocation location) {
+ updateBubbleBarLocationInternal(location, false /* animate */);
+ mSystemUiProxy.setBubbleBarLocation(location);
+ }
+
+ private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) {
+ mBubbleBarViewController.setBubbleBarLocation(location, animate);
+ mBubbleStashController.setBubbleBarLocation(location);
+ }
+
//
// Loading data for the bubbles
//
@@ -595,27 +615,39 @@
return mIconFactory.createBadgedIconBitmap(drawable).icon;
}
+ private void onBubbleBarBoundsChanged(Rect newBounds) {
+ Rect displayBounds = convertToDisplayBounds(newBounds);
+ // Only send bounds over if they changed
+ if (!displayBounds.equals(mLastSentBubbleBarBounds)) {
+ mLastSentBubbleBarBounds.set(displayBounds);
+ mSystemUiProxy.setBubbleBarBounds(displayBounds);
+ }
+ }
+
/**
* Get bounds of the bubble bar as if it would be expanded.
* Calculates the bounds instead of retrieving current view location as the view may be
* animating.
*/
private Rect getExpandedBubbleBarDisplayBounds() {
+ return convertToDisplayBounds(mBarView.getBubbleBarBounds());
+ }
+
+ private Rect convertToDisplayBounds(Rect currentBarBounds) {
Point displaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
- Rect currentBarBounds = mBarView.getBubbleBarBounds();
- Rect location = new Rect();
+ Rect displayBounds = new Rect();
// currentBarBounds is only useful for distance from left or right edge.
// It contains the current bounds, calculate the expanded bounds.
if (mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl())) {
- location.left = currentBarBounds.left;
- location.right = (int) (currentBarBounds.left + mBarView.expandedWidth());
+ displayBounds.left = currentBarBounds.left;
+ displayBounds.right = (int) (currentBarBounds.left + mBarView.expandedWidth());
} else {
- location.left = (int) (currentBarBounds.right - mBarView.expandedWidth());
- location.right = currentBarBounds.right;
+ displayBounds.left = (int) (currentBarBounds.right - mBarView.expandedWidth());
+ displayBounds.right = currentBarBounds.right;
}
final int translation = (int) abs(mBubbleStashController.getBubbleBarTranslationY());
- location.top = displaySize.y - currentBarBounds.height() - translation;
- location.bottom = displaySize.y - translation;
- return location;
+ displayBounds.top = displaySize.y - currentBarBounds.height() - translation;
+ displayBounds.bottom = displaySize.y - translation;
+ return displayBounds;
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 4ca7c89..711ba62 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -24,7 +24,9 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.LayoutDirection;
@@ -94,6 +96,8 @@
private final BubbleBarBackground mBubbleBarBackground;
+ private boolean mIsAnimatingNewBubble = false;
+
/**
* The current bounds of all the bubble bar. Note that these bounds may not account for
* translation. The bounds should be retrieved using {@link #getBubbleBarBounds()} which
@@ -108,6 +112,7 @@
private final float mIconSize;
// The elevation of the bubbles within the bar
private final float mBubbleElevation;
+ private final float mDragElevation;
private final int mPointerSize;
// Whether the bar is expanded (i.e. the bubble activity is being displayed).
@@ -138,11 +143,15 @@
@Nullable
private Consumer<String> mUpdateSelectedBubbleAfterCollapse;
+ private boolean mDragging;
+
@Nullable
private BubbleView mDraggedBubbleView;
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
+ private boolean mLocationChangePending;
+
public BubbleBarView(Context context) {
this(context, null);
}
@@ -163,6 +172,7 @@
mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
+ mDragElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size);
setClipToPadding(false);
@@ -237,12 +247,13 @@
}
}
+ @SuppressLint("RtlHardcoded")
private void onBubbleBarLocationChanged() {
+ mLocationChangePending = false;
final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
mBubbleBarBackground.setAnchorLeft(onLeft);
mRelativePivotX = onLeft ? 0f : 1f;
- ViewGroup.LayoutParams layoutParams = getLayoutParams();
- if (layoutParams instanceof LayoutParams lp) {
+ if (getLayoutParams() instanceof LayoutParams lp) {
lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
setLayoutParams(lp);
}
@@ -267,11 +278,82 @@
}
}
+ /**
+ * Set whether this view is being currently being dragged
+ */
+ public void setIsDragging(boolean dragging) {
+ if (mDragging == dragging) {
+ return;
+ }
+ mDragging = dragging;
+ setElevation(dragging ? mDragElevation : mBubbleElevation);
+ if (!dragging && mLocationChangePending) {
+ // During drag finish animation we may update the translation x value to shift the
+ // bubble to the new drop target. Clear the translation here.
+ setTranslationX(0f);
+ onBubbleBarLocationChanged();
+ }
+ }
+
+ /**
+ * Adjust resting position for the bubble bar while it is being dragged.
+ * <p>
+ * Bubble bar is laid out on left or right side of the screen. When it is being dragged to
+ * the opposite side, the resting position should be on that side. Calculate any additional
+ * translation that may be required to move the bubble bar to the new side.
+ *
+ * @param restingPosition relative resting position of the bubble bar from the laid out position
+ */
+ @SuppressLint("RtlHardcoded")
+ void adjustRelativeRestingPosition(PointF restingPosition) {
+ final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
+ // Bubble bar is placed left or right with gravity. Check where it is currently.
+ final int absoluteGravity = Gravity.getAbsoluteGravity(
+ ((LayoutParams) getLayoutParams()).gravity, getLayoutDirection());
+ final boolean gravityOnLeft =
+ (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT;
+
+ // Bubble bar is pinned to the same side per gravity and the desired location.
+ // Resting translation does not need to be adjusted.
+ if (locationOnLeft == gravityOnLeft) {
+ return;
+ }
+
+ // Bubble bar is laid out on left or right side of the screen. And the desired new
+ // location is on the other side. Calculate x translation value required to shift the
+ // bubble bar from one side to the other.
+ float x = getDistanceFromOtherSide();
+ if (locationOnLeft) {
+ // New location is on the left, shift left
+ // before -> |......ooo.| after -> |.ooo......|
+ restingPosition.x = -x;
+ } else {
+ // New location is on the right, shift right
+ // before -> |.ooo......| after -> |......ooo.|
+ restingPosition.x = x;
+ }
+ }
+
+ private float getDistanceFromOtherSide() {
+ // Calculate the shift needed to position the bubble bar on the other side
+ int displayWidth = getResources().getDisplayMetrics().widthPixels;
+ int margin = 0;
+ if (getLayoutParams() instanceof MarginLayoutParams lp) {
+ margin += lp.leftMargin;
+ margin += lp.rightMargin;
+ }
+ return (float) (displayWidth - getWidth() - margin);
+ }
+
private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
if (bubbleBarLocation != mBubbleBarLocation) {
mBubbleBarLocation = bubbleBarLocation;
- onBubbleBarLocationChanged();
- invalidate();
+ if (mDragging) {
+ mLocationChangePending = true;
+ } else {
+ onBubbleBarLocationChanged();
+ invalidate();
+ }
}
}
@@ -377,6 +459,7 @@
/** Prepares for animating a bubble while being stashed. */
public void prepareForAnimatingBubbleWhileStashed(String bubbleKey) {
+ mIsAnimatingNewBubble = true;
// we're about to animate the new bubble in. the new bubble has already been added to this
// view, but we're currently stashed, so before we can start the animation we need make
// everything else in the bubble bar invisible, except for the bubble that's being animated.
@@ -397,6 +480,9 @@
/** Resets the state after the bubble animation completed. */
public void onAnimatingBubbleCompleted() {
+ mIsAnimatingNewBubble = false;
+ // setting the background triggers relayout so no need to explicitly invalidate after the
+ // animation
setBackground(mBubbleBarBackground);
for (int i = 0; i < getChildCount(); i++) {
final BubbleView view = (BubbleView) getChildAt(i);
@@ -441,6 +527,12 @@
* on the expanded state.
*/
private void updateChildrenRenderNodeProperties() {
+ if (mIsAnimatingNewBubble) {
+ // don't update bubbles if a new bubble animation is playing.
+ // the bubble bar will redraw itself via onLayout after the animation.
+ return;
+ }
+
final float widthState = (float) mWidthAnimator.getAnimatedValue();
final float currentWidth = getWidth();
final float expandedWidth = expandedWidth();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 96d91ea..06769c5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -27,6 +27,7 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
@@ -84,6 +85,11 @@
private BubbleBarViewAnimator mBubbleBarViewAnimator;
+ @Nullable
+ private BubbleBarBoundsChangeListener mBoundsChangeListener;
+
+ private final Rect mPreviousBubbleBarBounds = new Rect();
+
public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
mActivity = activity;
mBarView = barView;
@@ -113,9 +119,17 @@
mBubbleBarClickListener = v -> onBubbleBarClicked();
mBubbleDragController.setupBubbleBarView(mBarView);
mBarView.setOnClickListener(mBubbleBarClickListener);
- mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
- );
+ mBarView.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+ Rect bubbleBarBounds = mBarView.getBubbleBarBounds();
+ if (!bubbleBarBounds.equals(mPreviousBubbleBarBounds)) {
+ mPreviousBubbleBarBounds.set(bubbleBarBounds);
+ if (mBoundsChangeListener != null) {
+ mBoundsChangeListener.onBoundsChanged(bubbleBarBounds);
+ }
+ }
+ });
mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
}
@@ -426,4 +440,19 @@
public void onDismissAllBubblesWhileDragging() {
mSystemUiProxy.removeAllBubbles();
}
+
+ /**
+ * Set listener to be notified when bubble bar bounds have changed
+ */
+ public void setBoundsChangeListener(@Nullable BubbleBarBoundsChangeListener listener) {
+ mBoundsChangeListener = listener;
+ }
+
+ /**
+ * Listener to receive updates about bubble bar bounds changing
+ */
+ public interface BubbleBarBoundsChangeListener {
+ /** Called when bounds have changed */
+ void onBoundsChanged(Rect newBounds);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 39440ba..8b811d9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -18,7 +18,6 @@
import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY;
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
import android.content.res.Resources;
import android.graphics.PointF;
@@ -42,11 +41,14 @@
private static final float SCALE_BUBBLE_FOCUSED = 1.2f;
private static final float SCALE_BUBBLE_CAPTURED = 0.9f;
private static final float SCALE_BUBBLE_BAR_FOCUSED = 1.1f;
+ // 400f matches to MEDIUM_LOW spring stiffness
+ private static final float TRANSLATION_SPRING_STIFFNESS = 400f;
private final PhysicsAnimator.SpringConfig mDefaultConfig =
new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY);
private final PhysicsAnimator.SpringConfig mTranslationConfig =
- new PhysicsAnimator.SpringConfig(STIFFNESS_MEDIUM, DAMPING_RATIO_LOW_BOUNCY);
+ new PhysicsAnimator.SpringConfig(TRANSLATION_SPRING_STIFFNESS,
+ DAMPING_RATIO_LOW_BOUNCY);
@NonNull
private final View mView;
@NonNull
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index dab7d9d..5ffc6d8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -25,7 +25,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarActivityContext;
/**
@@ -55,9 +54,8 @@
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDismissController = bubbleControllers.bubbleDismissController;
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
- mBubbleBarPinController.setListener(location -> {
- // TODO(b/330585397): update bubble bar location in shell
- });
+ mBubbleBarPinController.setListener(
+ bubbleControllers.bubbleBarController::updateBubbleBarLocation);
mBubbleDismissController.setListener(
stuck -> mBubbleBarPinController.setDropTargetHidden(stuck));
}
@@ -96,11 +94,8 @@
@SuppressLint("ClickableViewAccessibility")
public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) {
PointF initialRelativePivot = new PointF();
- final int restingElevation = bubbleBarView.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_elevation);
- final int dragElevation = bubbleBarView.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_drag_elevation);
bubbleBarView.setOnTouchListener(new BubbleTouchListener() {
+
@Override
protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
if (bubbleBarView.isExpanded()) return false;
@@ -114,7 +109,7 @@
// By default the bubble bar view pivot is in bottom right corner, while dragging
// it should be centered in order to align it with the dismiss target view
bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
- bubbleBarView.setElevation(dragElevation);
+ bubbleBarView.setIsDragging(true);
mBubbleBarPinController.onDragStart(
bubbleBarView.getBubbleBarLocation().isOnLeft(bubbleBarView.isLayoutRtl()));
}
@@ -138,7 +133,14 @@
void onDragEnd() {
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
- bubbleBarView.setElevation(restingElevation);
+ bubbleBarView.setIsDragging(false);
+ }
+
+ @Override
+ protected PointF getRestingPosition() {
+ PointF restingPosition = super.getRestingPosition();
+ bubbleBarView.adjustRelativeRestingPosition(restingPosition);
+ return restingPosition;
}
});
}
@@ -226,6 +228,13 @@
protected void onDragDismiss() {
}
+ /**
+ * Get the resting position of the view when drag is released
+ */
+ protected PointF getRestingPosition() {
+ return mViewInitialPosition;
+ }
+
@Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
@@ -353,7 +362,7 @@
mAnimator.animateDismiss(mViewInitialPosition, onComplete);
} else {
onDragRelease();
- mAnimator.animateToInitialState(mViewInitialPosition, getCurrentVelocity(),
+ mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(),
onComplete);
}
mBubbleDismissController.hideDismissView();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index e25e586..61a2e22 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -51,6 +51,15 @@
*/
private static final float STASHED_BAR_SCALE = 0.5f;
+ /** The duration of hiding and showing the stashed handle as part of a new bubble animation. */
+ private static final long NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS = 200;
+
+ /** The translation Y value the handle animates to when hiding it for a new bubble. */
+ private static final int NEW_BUBBLE_HIDE_HANDLE_ANIMATION_TRANSLATION_Y = -20;
+
+ /** The alpha value the handle animates to when hiding it for a new bubble. */
+ public static final float NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA = 0.5f;
+
protected final TaskbarActivityContext mActivity;
// Initialized in init.
@@ -64,6 +73,7 @@
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
private MultiPropertyFactory.MultiProperty mBubbleStashedHandleAlpha;
+ private AnimatedFloat mBubbleStashedHandleTranslationY;
private boolean mRequestedStashState;
private boolean mRequestedExpandedState;
@@ -95,6 +105,7 @@
mBubbleStashedHandleAlpha = mHandleViewController.getStashedHandleAlpha().get(
StashedHandleViewController.ALPHA_INDEX_STASHED);
+ mBubbleStashedHandleTranslationY = mHandleViewController.getStashedHandleTranslationY();
mStashedHeight = mHandleViewController.getStashedHeight();
mUnstashedHeight = mHandleViewController.getUnstashedHeight();
@@ -362,4 +373,35 @@
public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
mHandleViewController.setBubbleBarLocation(bubbleBarLocation);
}
+
+ /** Returns the x position of the center of the stashed handle. */
+ public float getStashedHandleCenterX() {
+ return mHandleViewController.getStashedHandleCenterX();
+ }
+
+ /** Returns the animation for hiding the handle before a new bubble animates in. */
+ public AnimatorSet buildHideHandleAnimationForNewBubble() {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(
+ mBubbleStashedHandleTranslationY.animateToValue(
+ NEW_BUBBLE_HIDE_HANDLE_ANIMATION_TRANSLATION_Y),
+ mBubbleStashedHandleAlpha.animateToValue(NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA));
+ animatorSet.setDuration(NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS);
+ return animatorSet;
+ }
+
+ /** Sets the alpha value of the stashed handle. */
+ public void setStashAlpha(float alpha) {
+ mBubbleStashedHandleAlpha.setValue(alpha);
+ }
+
+ /** Returns the animation for showing the handle after a new bubble animated in. */
+ public AnimatorSet buildShowHandleAnimationForNewBubble() {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(
+ mBubbleStashedHandleTranslationY.animateToValue(0),
+ mBubbleStashedHandleAlpha.animateToValue(1));
+ animatorSet.setDuration(NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS);
+ return animatorSet;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index f64517a..2a5912a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -29,6 +29,7 @@
import android.view.ViewOutlineProvider;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.taskbar.StashedHandleView;
@@ -47,7 +48,7 @@
private final TaskbarActivityContext mActivity;
private final StashedHandleView mStashedHandleView;
- private final MultiValueAlpha mTaskbarStashedHandleAlpha;
+ private final MultiValueAlpha mStashedHandleAlpha;
// Initialized in init.
private BubbleBarViewController mBarViewController;
@@ -58,6 +59,12 @@
private int mStashedHandleWidth;
private int mStashedHandleHeight;
+ private final AnimatedFloat mStashedHandleTranslationY =
+ new AnimatedFloat(this::updateTranslationY);
+
+ // Modified when swipe up is happening on the stashed handle or task bar.
+ private float mSwipeUpTranslationY;
+
// The bounds we want to clip to in the settled state when showing the stashed handle.
private final Rect mStashedHandleBounds = new Rect();
@@ -75,7 +82,7 @@
StashedHandleView stashedHandleView) {
mActivity = activity;
mStashedHandleView = stashedHandleView;
- mTaskbarStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
+ mStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
}
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
@@ -93,7 +100,7 @@
R.dimen.transient_taskbar_bottom_margin);
mStashedHandleView.getLayoutParams().height = mBarSize + bottomMargin;
- mTaskbarStashedHandleAlpha.get(0).setValue(0);
+ mStashedHandleAlpha.get(0).setValue(0);
mStashedTaskbarHeight = resources.getDimensionPixelSize(
R.dimen.bubblebar_stashed_size);
@@ -231,18 +238,33 @@
}
}
+ /** Returns an animator for translation Y. */
+ public AnimatedFloat getStashedHandleTranslationY() {
+ return mStashedHandleTranslationY;
+ }
+
/**
* Sets the translation of the stashed handle during the swipe up gesture.
*/
public void setTranslationYForSwipe(float transY) {
- mStashedHandleView.setTranslationY(transY);
+ mSwipeUpTranslationY = transY;
+ updateTranslationY();
+ }
+
+ private void updateTranslationY() {
+ mStashedHandleView.setTranslationY(mStashedHandleTranslationY.value + mSwipeUpTranslationY);
}
/**
* Used by {@link BubbleStashController} to animate the handle when stashing or un stashing.
*/
public MultiPropertyFactory<View> getStashedHandleAlpha() {
- return mTaskbarStashedHandleAlpha;
+ return mStashedHandleAlpha;
+ }
+
+ /** Returns the x position of the center of the stashed handle. */
+ public float getStashedHandleCenterX() {
+ return mStashedHandleBounds.exactCenterX();
}
/**
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 bcb9f4d..1db5103 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -18,12 +18,16 @@
import android.view.View
import android.view.View.VISIBLE
+import androidx.core.animation.AnimatorSet
+import androidx.core.animation.ObjectAnimator
+import androidx.core.animation.doOnEnd
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce
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.systemui.util.doOnEnd
import com.android.wm.shell.shared.animation.PhysicsAnimator
/** Handles animations for bubble bar bubbles. */
@@ -39,7 +43,17 @@
/** The time to show the flyout. */
const val FLYOUT_DELAY_MS: Long = 2500
/** The translation Y the new bubble will animate to. */
- const val BUBBLE_ANIMATION_TRANSLATION_Y = -50f
+ const val BUBBLE_ANIMATION_FINAL_TRANSLATION_Y = -50f
+ /** The initial translation Y value the new bubble is set to before the animation starts. */
+ // TODO(liranb): get rid of this and calculate this based on the y-distance between the
+ // bubble and the stash handle.
+ const val BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y = 50f
+ /** The initial scale Y value that the new bubble is set to before the animation starts. */
+ const val BUBBLE_ANIMATION_INITIAL_SCALE_Y = 0.3f
+ /** The initial alpha value that the new bubble is set to before the animation starts. */
+ const val BUBBLE_ANIMATION_INITIAL_ALPHA = 0.5f
+ /** The duration of the hide bubble animation. */
+ const val HIDE_BUBBLE_ANIMATION_DURATION_MS = 250L
}
/** An interface for scheduling jobs. */
@@ -78,41 +92,94 @@
// the animation of a new bubble is divided into 2 parts. The first part shows the bubble
// and the second part hides it after a delay.
val showAnimation = buildShowAnimation(bubbleView, b.key, animator)
- val hideAnimation = buildHideAnimation(animator)
+ val hideAnimation = buildHideAnimation(bubbleView)
scheduler.post(showAnimation)
scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
}
- /** Returns a lambda that starts the animation that shows the new bubble. */
+ /**
+ * Returns a lambda that starts the animation that shows the new bubble.
+ *
+ * The animation is divided into 2 parts. First the stash handle starts animating up and fades
+ * out. When it ends the bubble starts fading in. The bubble and stashed handle are aligned to
+ * give the impression of the stash handle morphing into the bubble.
+ */
private fun buildShowAnimation(
bubbleView: BubbleView,
key: String,
- animator: PhysicsAnimator<BubbleView>
+ bubbleAnimator: PhysicsAnimator<BubbleView>
): () -> Unit = {
+ // calculate the initial translation x the bubble should have in order to align it with the
+ // stash handle.
+ val initialTranslationX =
+ bubbleStashController.stashedHandleCenterX - bubbleView.centerXOnScreen
bubbleBarView.prepareForAnimatingBubbleWhileStashed(key)
- animator.setDefaultSpringConfig(springConfig)
- animator
+ bubbleAnimator.setDefaultSpringConfig(springConfig)
+ bubbleAnimator
.spring(DynamicAnimation.ALPHA, 1f)
- .spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_TRANSLATION_Y)
+ .spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_FINAL_TRANSLATION_Y)
+ .spring(DynamicAnimation.SCALE_Y, 1f)
+ // prepare the bubble for the animation
bubbleView.alpha = 0f
+ bubbleView.translationX = initialTranslationX
+ bubbleView.translationY = BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y
+ bubbleView.scaleY = BUBBLE_ANIMATION_INITIAL_SCALE_Y
bubbleView.visibility = VISIBLE
- animator.start()
+ // start the stashed handle animation. when it ends, start the bubble animation.
+ val stashedHandleAnimation = bubbleStashController.buildHideHandleAnimationForNewBubble()
+ stashedHandleAnimation.doOnEnd {
+ bubbleView.alpha = BUBBLE_ANIMATION_INITIAL_ALPHA
+ bubbleAnimator.start()
+ bubbleStashController.setStashAlpha(0f)
+ }
+ stashedHandleAnimation.start()
}
- /** Returns a lambda that starts the animation that hides the new bubble. */
- private fun buildHideAnimation(animator: PhysicsAnimator<BubbleView>): () -> Unit = {
- animator.setDefaultSpringConfig(springConfig)
- animator
- .spring(DynamicAnimation.ALPHA, 0f)
- .spring(DynamicAnimation.TRANSLATION_Y, 0f)
- .addEndListener { _, _, _, canceled, _, _, allRelevantPropertyAnimsEnded ->
- if (!canceled && allRelevantPropertyAnimsEnded) {
- if (bubbleStashController.isStashed) {
- bubbleBarView.alpha = 0f
- }
- bubbleBarView.onAnimatingBubbleCompleted()
- }
+ /**
+ * Returns a lambda that starts the animation that hides the new bubble.
+ *
+ * Similarly to the show animation, this is divided into 2 parts. We first animate the bubble
+ * out, and then animate the stash handle in. At the end of the animation we reset the values of
+ * the bubble.
+ */
+ private fun buildHideAnimation(bubbleView: BubbleView): () -> Unit = {
+ val stashAnimation = bubbleStashController.buildShowHandleAnimationForNewBubble()
+ val alphaAnimator =
+ ObjectAnimator.ofFloat(bubbleView, View.ALPHA, BUBBLE_ANIMATION_INITIAL_ALPHA)
+ val translationYAnimator =
+ ObjectAnimator.ofFloat(
+ bubbleView,
+ View.TRANSLATION_Y,
+ BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y
+ )
+ val scaleYAnimator =
+ ObjectAnimator.ofFloat(bubbleView, View.SCALE_Y, BUBBLE_ANIMATION_INITIAL_SCALE_Y)
+ val hideBubbleAnimation = AnimatorSet()
+ hideBubbleAnimation.playTogether(alphaAnimator, translationYAnimator, scaleYAnimator)
+ hideBubbleAnimation.duration = HIDE_BUBBLE_ANIMATION_DURATION_MS
+ hideBubbleAnimation.doOnEnd {
+ // the bubble is now hidden, start the stash handle animation and reset bubble
+ // properties
+ bubbleStashController.setStashAlpha(
+ BubbleStashController.NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA
+ )
+ bubbleView.alpha = 0f
+ stashAnimation.start()
+ bubbleView.translationY = 0f
+ bubbleView.scaleY = 1f
+ if (bubbleStashController.isStashed) {
+ bubbleBarView.alpha = 0f
}
- animator.start()
+ bubbleBarView.onAnimatingBubbleCompleted()
+ }
+ hideBubbleAnimation.start()
}
}
+
+/** The X position in screen coordinates of the center of the bubble. */
+private val BubbleView.centerXOnScreen: Float
+ get() {
+ val screenCoordinates = IntArray(2)
+ getLocationOnScreen(screenCoordinates)
+ return screenCoordinates[0] + width / 2f
+ }
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 9840791..34d3fad 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -28,35 +28,39 @@
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.systemui.shared.rotation.RotationButton
-/**
- * Layoutter for rendering task bar in large screen, both in 3-button and gesture nav mode.
- */
+/** Layoutter for rendering task bar in large screen, both in 3-button and gesture nav mode. */
class TaskbarNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup,
- imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
- a11yButton: ImageView?,
- space: Space?
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?,
+ space: Space?
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- rotationButton,
- a11yButton,
- space
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton,
+ space
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
// Add spacing after the end of the last nav button
- var navMarginEnd = resources
- .getDimension(context.deviceProfile.inv.inlineNavButtonsEndSpacing)
- .toInt()
+ var navMarginEnd =
+ resources.getDimension(context.deviceProfile.inv.inlineNavButtonsEndSpacing).toInt()
+
+ val cutout = context.display.cutout
+ val bottomRect = cutout?.boundingRectBottom
+ if (bottomRect != null && !bottomRect.isEmpty) {
+ navMarginEnd = bottomRect.width()
+ }
+
val contextualWidth = endContextualContainer.width
// If contextual buttons are showing, we check if the end margin is enough for the
// contextual button to be showing - if not, move the nav buttons over a smidge
@@ -65,8 +69,11 @@
navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2
}
- val navButtonParams = FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ val navButtonParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
navButtonParams.apply {
gravity = Gravity.END or Gravity.CENTER_VERTICAL
marginEnd = navMarginEnd
@@ -98,18 +105,28 @@
startContextualContainer.removeAllViews()
if (!context.deviceProfile.isGestureMode) {
- val contextualMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_padding)
+ val contextualMargin =
+ resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_padding)
repositionContextualContainer(endContextualContainer, WRAP_CONTENT, 0, 0, Gravity.END)
- repositionContextualContainer(startContextualContainer, WRAP_CONTENT, contextualMargin,
- contextualMargin, Gravity.START)
+ repositionContextualContainer(
+ startContextualContainer,
+ WRAP_CONTENT,
+ contextualMargin,
+ contextualMargin,
+ Gravity.START
+ )
if (imeSwitcher != null) {
- val imeStartMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_ime_switcher_button_margin_start)
+ val imeStartMargin =
+ resources.getDimensionPixelSize(
+ R.dimen.taskbar_ime_switcher_button_margin_start
+ )
startContextualContainer.addView(imeSwitcher)
- val imeSwitcherButtonParams = FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ val imeSwitcherButtonParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
imeSwitcherButtonParams.apply {
marginStart = imeStartMargin
gravity = Gravity.CENTER_VERTICAL
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index b49c752..c2d3d2c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -62,8 +62,8 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
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.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
@@ -105,7 +105,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
-import com.android.launcher3.HomeTransitionController;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -217,7 +216,7 @@
private FixedContainerItems mAllAppsPredictions;
private HotseatPredictionController mHotseatPredictionController;
private DepthController mDepthController;
- private DesktopVisibilityController mDesktopVisibilityController;
+ private @Nullable DesktopVisibilityController mDesktopVisibilityController;
private QuickstepTransitionManager mAppTransitionManager;
private OverviewActionsView mActionsView;
private TISBindHelper mTISBindHelper;
@@ -245,8 +244,6 @@
private boolean mIsPredictiveBackToHomeInProgress;
- private HomeTransitionController mHomeTransitionController;
-
@Override
protected void setupViews() {
super.setupViews();
@@ -277,15 +274,10 @@
mAppTransitionManager.registerRemoteAnimations();
mAppTransitionManager.registerRemoteTransitions();
- if (FeatureFlags.enableHomeTransitionListener()) {
- mHomeTransitionController = new HomeTransitionController();
- mHomeTransitionController.registerHomeTransitionListener(this);
- }
-
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
- mDesktopVisibilityController = new DesktopVisibilityController(this);
if (enableDesktopWindowingMode()) {
+ mDesktopVisibilityController = new DesktopVisibilityController(this);
mDesktopVisibilityController.registerSystemUiListener();
mSplitSelectStateController.initSplitFromDesktopController(this);
}
@@ -523,10 +515,6 @@
mLauncherUnfoldAnimationController.onDestroy();
}
- if (mHomeTransitionController != null) {
- mHomeTransitionController.unregisterHomeTransitionListener();
- }
-
if (mDesktopVisibilityController != null) {
mDesktopVisibilityController.unregisterSystemUiListener();
}
@@ -948,15 +936,13 @@
@Override
public void setResumed() {
- if (enableDesktopWindowingMode()) {
- DesktopVisibilityController controller = mDesktopVisibilityController;
- if (controller != null && controller.areFreeformTasksVisible()
- && !controller.isRecentsGestureInProgress()) {
- // Return early to skip setting activity to appear as resumed
- // TODO(b/255649902): shouldn't be needed when we have a separate launcher state
- // for desktop that we can use to control other parts of launcher
- return;
- }
+ if (mDesktopVisibilityController != null
+ && mDesktopVisibilityController.areFreeformTasksVisible()
+ && !mDesktopVisibilityController.isRecentsGestureInProgress()) {
+ // Return early to skip setting activity to appear as resumed
+ // TODO(b/255649902): shouldn't be needed when we have a separate launcher state
+ // for desktop that we can use to control other parts of launcher
+ return;
}
super.setResumed();
}
@@ -1090,6 +1076,7 @@
return mDepthController;
}
+ @Nullable
public DesktopVisibilityController getDesktopVisibilityController() {
return mDesktopVisibilityController;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 7875dae..2625919 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -68,10 +68,13 @@
@Override
public void onBackInvoked(Launcher launcher) {
// In predictive back swipe, onBackInvoked() will be called after onBackStarted().
- // Because the 2nd InteractionJankMonitor.begin() will be ignore within timeout, it's safe
- // to call InteractionJankMonitorWrapper.begin here.
- InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
- Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ // In 3 button mode, onBackStarted() is not called but onBackInvoked() will be called.
+ // Thus In onBackInvoked(), we should only begin instrumenting if we didn't call
+ // onBackStarted() to start instrumenting CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK.
+ if (!InteractionJankMonitorWrapper.isInstrumenting(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK)) {
+ InteractionJankMonitorWrapper.begin(
+ launcher.getAppsView(), Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
+ }
super.onBackInvoked(launcher);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index a443c00..547de77 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.content.Context;
import android.graphics.Color;
@@ -92,8 +91,7 @@
@Override
protected float getDepthUnchecked(Context context) {
- if (enableDesktopWindowingMode()
- && Launcher.getLauncher(context).areFreeformTasksVisible()) {
+ if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
// Don't blur the background while freeform tasks are visible
return BaseDepthController.DEPTH_0_PERCENT;
} else if (enableScalingRevealHomeAnimation()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 2587395..7fb811d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.graphics.Color;
@@ -46,11 +45,9 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- if (enableDesktopWindowingMode()) {
- if (launcher.areFreeformTasksVisible()) {
- // No scrim while freeform tasks are visible
- return Color.TRANSPARENT;
- }
+ if (launcher.areFreeformTasksVisible()) {
+ // No scrim while freeform tasks are visible
+ return Color.TRANSPARENT;
}
DeviceProfile dp = launcher.getDeviceProfile();
if (dp.isTaskbarPresentInApps) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index dc1c6a6..62e823a 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -61,7 +61,6 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -947,7 +946,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- if (enableDesktopWindowingMode() && targets.hasDesktopTasks()) {
+ if (targets.hasDesktopTasks()) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
@@ -1170,13 +1169,11 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
- if (enableDesktopWindowingMode()) {
+ DesktopVisibilityController desktopVisibilityController =
+ mActivityInterface.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
// Notify the SysUI to stash desktop apps if they are visible
- DesktopVisibilityController desktopVisibilityController =
- mActivityInterface.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- desktopVisibilityController.onHomeActionTriggered();
- }
+ desktopVisibilityController.onHomeActionTriggered();
}
break;
case RECENTS:
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 24c99e3..a3f6be0 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -25,7 +25,6 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -109,19 +108,17 @@
if (endTarget != null) {
// We were on our way to this state when we got canceled, end there instead.
startState = stateFromGestureEndTarget(endTarget);
- if (enableDesktopWindowingMode()) {
- DesktopVisibilityController controller = getDesktopVisibilityController();
- if (controller != null && controller.areFreeformTasksVisible()
- && endTarget == LAST_TASK) {
- // When we are cancelling the transition and going back to last task, move to
- // rest state instead when desktop tasks are visible.
- // If a fullscreen task is visible, launcher goes to normal state when the
- // activity is stopped. This does not happen when freeform tasks are visible
- // on top of launcher. Force the launcher state to rest state here.
- startState = activity.getStateManager().getRestState();
- // Do not animate the transition
- activityVisible = false;
- }
+ DesktopVisibilityController controller = getDesktopVisibilityController();
+ if (controller != null && controller.areFreeformTasksVisible()
+ && endTarget == LAST_TASK) {
+ // When we are cancelling the transition and going back to last task, move to
+ // rest state instead when desktop tasks are visible.
+ // If a fullscreen task is visible, launcher goes to normal state when the
+ // activity is stopped. This does not happen when freeform tasks are visible
+ // on top of launcher. Force the launcher state to rest state here.
+ startState = activity.getStateManager().getRestState();
+ // Do not animate the transition
+ activityVisible = false;
}
}
activity.getStateManager().goToState(startState, activityVisible);
diff --git a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
new file mode 100644
index 0000000..241e16d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/HomeVisibilityState.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.quickstep
+
+import android.os.RemoteException
+import android.util.Log
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.util.Executors
+import com.android.wm.shell.shared.IHomeTransitionListener.Stub
+import com.android.wm.shell.shared.IShellTransitions
+
+/** Class to track visibility state of Launcher */
+class HomeVisibilityState {
+
+ var isHomeVisible = true
+ private set
+
+ private var listeners = mutableSetOf<VisibilityChangeListener>()
+
+ fun addListener(l: VisibilityChangeListener) = listeners.add(l)
+
+ fun removeListener(l: VisibilityChangeListener) = listeners.remove(l)
+
+ fun init(transitions: IShellTransitions?) {
+ if (!FeatureFlags.enableHomeTransitionListener()) return
+ try {
+ transitions?.setHomeTransitionListener(
+ object : Stub() {
+ override fun onHomeVisibilityChanged(isVisible: Boolean) {
+ Executors.MAIN_EXECUTOR.execute {
+ isHomeVisible = isVisible
+ listeners.forEach { it.onHomeVisibilityChanged(isVisible) }
+ }
+ }
+ }
+ )
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed call setHomeTransitionListener", e)
+ }
+ }
+
+ interface VisibilityChangeListener {
+ fun onHomeVisibilityChanged(isVisible: Boolean)
+ }
+
+ override fun toString() = "{HomeVisibilityState isHomeVisible=$isHomeVisible}"
+
+ companion object {
+
+ private const val TAG = "HomeVisibilityState"
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 97c48e6..7c17e4e 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -35,6 +35,7 @@
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
@@ -206,8 +207,17 @@
@UiThread
private Launcher getVisibleLauncher() {
Launcher launcher = getCreatedActivity();
- return (launcher != null) && launcher.isStarted()
- && (isInLiveTileMode() || launcher.hasBeenResumed()) ? launcher : null;
+ if (launcher == null) {
+ return null;
+ }
+ if (launcher.isStarted() && (isInLiveTileMode() || launcher.hasBeenResumed())) {
+ return launcher;
+ }
+ if (Flags.useActivityOverlay()
+ && SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()) {
+ return launcher;
+ }
+ return null;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 2d25295..79b09fd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -39,6 +39,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
+import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
@@ -99,7 +100,7 @@
private final int mWindowScaleMarginX;
private float mWindowScaleEndCornerRadius;
private float mWindowScaleStartCornerRadius;
- private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
+ private final Interpolator mProgressInterpolator = Interpolators.BACK_GESTURE;
private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
private final PointF mInitialTouchPos = new PointF();
@@ -302,7 +303,7 @@
if (mScrimLayer == null) {
addScrimLayer();
}
- mTransaction.apply();
+ applyTransaction();
}
private void setLauncherTargetViewVisible(boolean isVisible) {
@@ -342,7 +343,8 @@
return;
}
if (mScrimLayer.isValid()) {
- mTransaction.remove(mScrimLayer).apply();
+ mTransaction.remove(mScrimLayer);
+ applyTransaction();
}
mScrimLayer = null;
}
@@ -396,7 +398,11 @@
mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
}
+ applyTransaction();
+ }
+ private void applyTransaction() {
+ mTransaction.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
mTransaction.apply();
}
@@ -511,7 +517,7 @@
float value = (Float) animation.getAnimatedValue();
if (mScrimLayer != null && mScrimLayer.isValid()) {
mTransaction.setAlpha(mScrimLayer, value * mScrimAlpha);
- mTransaction.apply();
+ applyTransaction();
}
});
mScrimAlphaAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 4c1b1e0..879fccb 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -270,9 +270,13 @@
int numVisibleTasks = 0;
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (enableDesktopWindowingMode() && rawTask.getType() == TYPE_FREEFORM) {
- GroupTask desktopTask = createDesktopTask(rawTask);
- allTasks.add(desktopTask);
+ if (rawTask.getType() == TYPE_FREEFORM) {
+ // TYPE_FREEFORM tasks is only created when enableDesktopWindowingMode() is true,
+ // leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
+ if (enableDesktopWindowingMode()) {
+ GroupTask desktopTask = createDesktopTask(rawTask);
+ allTasks.add(desktopTask);
+ }
continue;
}
ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index ffbb064..0ce4d0a 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,7 +17,6 @@
package com.android.quickstep;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
import android.app.WindowConfiguration;
@@ -68,17 +67,15 @@
* running tasks
*/
public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
- if (enableDesktopWindowingMode()) {
- DesktopVisibilityController desktopVisibilityController =
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
- if (desktopVisibilityController != null) {
- int visibleTasksCount = desktopVisibilityController.getVisibleFreeformTasksCount();
- if (visibleTasksCount > 0) {
- // Allocate +1 to account for a new task added to the desktop mode
- int numHandles = visibleTasksCount + 1;
- init(context, sizingStrategy, numHandles, true /* forDesktop */);
- return;
- }
+ DesktopVisibilityController desktopVisibilityController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ int visibleTasksCount = desktopVisibilityController.getVisibleFreeformTasksCount();
+ if (visibleTasksCount > 0) {
+ // Allocate +1 to account for a new task added to the desktop mode
+ int numHandles = visibleTasksCount + 1;
+ init(context, sizingStrategy, numHandles, true /* forDesktop */);
+ return;
}
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index b6272da..ab609fd 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -63,7 +63,6 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.util.ScreenshotRequest;
import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.ActiveGestureLog;
@@ -82,6 +81,7 @@
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
@@ -91,7 +91,6 @@
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.IShellTransitions;
import com.android.wm.shell.splitscreen.ISplitScreen;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
@@ -157,7 +156,7 @@
private IOnBackInvokedCallback mBackToLauncherCallback;
private IRemoteAnimationRunner mBackToLauncherRunner;
private IDragAndDrop mDragAndDrop;
- private IHomeTransitionListener mHomeTransitionListener;
+ private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -269,7 +268,7 @@
setBubblesListener(mBubblesListener);
registerSplitScreenListener(mSplitScreenListener);
registerSplitSelectListener(mSplitSelectListener);
- setHomeTransitionListener(mHomeTransitionListener);
+ mHomeVisibilityState.init(mShellTransitions);
setStartingWindowListener(mStartingWindowListener);
setLauncherUnlockAnimationController(
mLauncherActivityClass, mLauncherUnlockAnimationController);
@@ -455,6 +454,17 @@
}
@Override
+ public void setOverrideHomeButtonLongPress(long duration, float slopMultiplier) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setOverrideHomeButtonLongPress(duration, slopMultiplier);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setOverrideHomeButtonLongPress", e);
+ }
+ }
+ }
+
+ @Override
public void notifyAccessibilityButtonClicked(int displayId) {
if (mSystemUiProxy != null) {
try {
@@ -808,6 +818,30 @@
}
}
+ /**
+ * Tells SysUI to update the bubble bar location to the new location.
+ * @param location new location for the bubble bar
+ */
+ public void setBubbleBarLocation(BubbleBarLocation location) {
+ try {
+ mBubbles.setBubbleBarLocation(location);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setBubbleBarLocation");
+ }
+ }
+
+ /**
+ * Tells SysUI the bounds for the bubble bar
+ * @param bubbleBarBounds bounds of the bubble bar in display coordinates
+ */
+ public void setBubbleBarBounds(Rect bubbleBarBounds) {
+ try {
+ mBubbles.setBubbleBarBounds(bubbleBarBounds);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setBubbleBarBounds");
+ }
+ }
+
//
// Splitscreen
//
@@ -1102,22 +1136,8 @@
mRemoteTransitions.remove(remoteTransition);
}
- public void setHomeTransitionListener(IHomeTransitionListener listener) {
- if (!FeatureFlags.enableHomeTransitionListener()) {
- return;
- }
-
- mHomeTransitionListener = listener;
-
- if (mShellTransitions != null) {
- try {
- mShellTransitions.setHomeTransitionListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setHomeTransitionListener", e);
- }
- } else {
- Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null");
- }
+ public HomeVisibilityState getHomeVisibilityState() {
+ return mHomeVisibilityState;
}
/**
@@ -1558,7 +1578,7 @@
pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
pw.println("\tmOneHanded=" + mOneHanded);
pw.println("\tmShellTransitions=" + mShellTransitions);
- pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener);
+ pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
pw.println("\tmStartingWindow=" + mStartingWindow);
pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index e4a8619..e22703b 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -34,6 +34,7 @@
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.TopTaskTracker;
+import com.android.quickstep.util.AssistStateManager;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -64,8 +65,9 @@
super(delegate, inputMonitor);
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
mDeepPressEnabled = FeatureFlags.ENABLE_LPNH_DEEP_PRESS.get();
- if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
- mLongPressTimeout = FeatureFlags.LPNH_TIMEOUT_MS.get();
+ AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(context);
+ if (assistStateManager.getLPNHDurationMillis().isPresent()) {
+ mLongPressTimeout = assistStateManager.getLPNHDurationMillis().get().intValue();
} else {
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
}
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index f4da867..59bf105 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -153,7 +153,7 @@
IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
MODEL_EXECUTOR.execute(() -> {
- newAppPair.getContents().forEach(member -> {
+ newAppPair.getAppContents().forEach(member -> {
member.title = "";
member.bitmap = iconCache.getDefaultIcon(newAppPair.user);
iconCache.getTitleAndIcon(member, member.usingLowResIcon());
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
index a854656..a1fdbbb 100644
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -52,6 +52,16 @@
return Optional.empty();
}
+ /** Get the Launcher overridden long press duration to trigger Assistant. */
+ public Optional<Long> getLPNHDurationMillis() {
+ return Optional.empty();
+ }
+
+ /** Get the Launcher overridden long press touch slop multiplier to trigger Assistant. */
+ public Optional<Long> getLPNHCustomSlopMultiplier() {
+ return Optional.empty();
+ }
+
/** Return {@code true} if the Settings toggle is enabled. */
public boolean isSettingsAllEntrypointsEnabled() {
return false;
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 6b27004..a2d3859 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -671,6 +671,7 @@
appIcon2,
dividerPos
)
+ floatingView.bringToFront()
// Launcher animation: animate the floating view, expanding to fill the display surface
progressUpdater.addUpdateListener(
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d8cbbf9..bff5a25 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -35,7 +35,6 @@
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDINGINTENT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
@@ -70,6 +69,7 @@
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.launcher3.Launcher;
@@ -132,6 +132,7 @@
private final StatsLogManager mStatsLogManager;
private final SystemUiProxy mSystemUiProxy;
private final StateManager mStateManager;
+ @Nullable
private SplitFromDesktopController mSplitFromDesktopController;
@Nullable
private DepthController mDepthController;
@@ -208,6 +209,9 @@
mActivityBackCallback = null;
mAppPairsController.onDestroy();
mSplitSelectDataHolder.onDestroy();
+ if (mSplitFromDesktopController != null) {
+ mSplitFromDesktopController.onDestroy();
+ }
}
/**
@@ -643,7 +647,12 @@
}
public void initSplitFromDesktopController(Launcher launcher) {
- mSplitFromDesktopController = new SplitFromDesktopController(launcher);
+ initSplitFromDesktopController(new SplitFromDesktopController(launcher));
+ }
+
+ @VisibleForTesting
+ void initSplitFromDesktopController(SplitFromDesktopController controller) {
+ mSplitFromDesktopController = controller;
}
private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId,
@@ -968,7 +977,6 @@
@Override
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
- if (!enableDesktopWindowingMode()) return false;
MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
taskBounds));
return true;
@@ -977,6 +985,11 @@
SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener);
}
+ void onDestroy() {
+ SystemUiProxy.INSTANCE.get(mLauncher).unregisterSplitSelectListener(
+ mSplitSelectListener);
+ }
+
/**
* Enter split select from desktop mode.
* @param taskInfo the desktop task to move to split stage
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 2282c46..16d707b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -17,7 +17,6 @@
package com.android.quickstep.util;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -197,8 +196,6 @@
}
private boolean shouldIgnoreSecondSplitLaunch() {
- return (!FeatureFlags.enableSplitContextually()
- && !enableDesktopWindowingMode())
- || !mController.isSplitSelectActive();
+ return !FeatureFlags.enableSplitContextually() || !mController.isSplitSelectActive();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index cd4fab6..0a3d2a0 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -26,7 +26,6 @@
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.enableDesktopWindowingMode;
import android.annotation.TargetApi;
import android.content.Context;
@@ -265,11 +264,11 @@
@Override
public void onGestureAnimationEnd() {
- DesktopVisibilityController desktopVisibilityController = null;
+ DesktopVisibilityController desktopVisibilityController =
+ mActivity.getDesktopVisibilityController();
boolean showDesktopApps = false;
GestureState.GestureEndTarget endTarget = null;
- if (enableDesktopWindowingMode()) {
- desktopVisibilityController = mActivity.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
endTarget = mCurrentGestureEndTarget;
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
&& desktopVisibilityController.areFreeformTasksVisible()) {
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 384a8d8..5188d4a 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -24,6 +24,7 @@
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -107,6 +108,7 @@
private MultiValueAlpha mMultiValueAlpha;
+ protected LinearLayout mActionButtons;
// The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is an
// ImageButton in go launcher (does not share a common class with Button). Take care when
// casting this.
@@ -151,7 +153,8 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS);
+ mActionButtons = findViewById(R.id.action_buttons);
+ mMultiValueAlpha = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
mMultiValueAlpha.setUpdateVisibility(true);
mScreenshotButton = findViewById(R.id.action_screenshot);
@@ -243,13 +246,13 @@
/**
* Updates a batch of flags to hide and show actions buttons for tablet/non tablet case.
- * @param isSmallScreen True if the current display is a small screen.
*/
- public void updateForSmallScreen(boolean isSmallScreen) {
+ private void updateForIsTablet() {
+ assert mDp != null;
// Update flags to see if split button should be hidden.
- updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, isSmallScreen);
+ updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, !mDp.isTablet);
// Update flags to see if save app pair button should be hidden.
- updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, isSmallScreen);
+ updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, !mDp.isTablet);
}
/**
@@ -274,7 +277,10 @@
mScreenshotButtonHiddenFlags &= ~flag;
}
int desiredVisibility = mScreenshotButtonHiddenFlags == 0 ? VISIBLE : GONE;
- mScreenshotButton.setVisibility(desiredVisibility);
+ if (mScreenshotButton.getVisibility() != desiredVisibility) {
+ mScreenshotButton.setVisibility(desiredVisibility);
+ mActionButtons.requestLayout();
+ }
}
/**
@@ -292,8 +298,10 @@
mSplitButtonHiddenFlags &= ~flag;
}
int desiredVisibility = mSplitButtonHiddenFlags == 0 ? VISIBLE : GONE;
- mSplitButton.setVisibility(desiredVisibility);
- findViewById(R.id.action_split_space).setVisibility(desiredVisibility);
+ if (mSplitButton.getVisibility() != desiredVisibility) {
+ mSplitButton.setVisibility(desiredVisibility);
+ mActionButtons.requestLayout();
+ }
}
/**
@@ -315,7 +323,10 @@
mAppPairButtonHiddenFlags &= ~flag;
}
int desiredVisibility = mAppPairButtonHiddenFlags == 0 ? VISIBLE : GONE;
- mSaveAppPairButton.setVisibility(desiredVisibility);
+ if (mSaveAppPairButton.getVisibility() != desiredVisibility) {
+ mSaveAppPairButton.setVisibility(desiredVisibility);
+ mActionButtons.requestLayout();
+ }
}
public MultiProperty getContentAlpha() {
@@ -342,7 +353,7 @@
* Returns the visibility of the overview actions buttons.
*/
public @Visibility int getActionsButtonVisibility() {
- return findViewById(R.id.action_buttons).getVisibility();
+ return mActionButtons.getVisibility();
}
/**
@@ -358,8 +369,7 @@
if (mDp == null) {
return;
}
- LayoutParams actionParams = (LayoutParams) findViewById(
- R.id.action_buttons).getLayoutParams();
+ LayoutParams actionParams = (LayoutParams) mActionButtons.getLayoutParams();
actionParams.setMargins(
actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
actionParams.rightMargin, getBottomMargin());
@@ -386,6 +396,7 @@
mDp = dp;
mTaskSize.set(taskSize);
updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
+ updateForIsTablet();
requestLayout();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2aa16a9..eca70b7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1061,6 +1061,8 @@
@Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
mActionsView = actionsView;
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
+ // Update flags for 1p/3p launchers
+ mActionsView.updateFor3pLauncher(!supportsAppPairs());
mSplitSelectStateController = splitController;
mDesktopRecentsTransitionController = desktopRecentsTransitionController;
}
@@ -1251,16 +1253,23 @@
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
appAnimator.setInterpolator(ACCELERATE_DECELERATE);
+ final Matrix matrix = new Matrix();
appAnimator.addUpdateListener(valueAnimator -> {
float percent = valueAnimator.getAnimatedFraction();
SurfaceTransaction transaction = new SurfaceTransaction();
- Matrix matrix = new Matrix();
- matrix.postScale(percent, percent);
- matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
- mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
- transaction.forSurface(apps[apps.length - 1].leash)
- .setAlpha(percent)
- .setMatrix(matrix);
+ for (int i = apps.length - 1; i >= 0; --i) {
+ RemoteAnimationTarget app = apps[i];
+
+ float dx = mActivity.getDeviceProfile().widthPx * (1 - percent) / 2
+ + app.screenSpaceBounds.left * percent;
+ float dy = mActivity.getDeviceProfile().heightPx * (1 - percent) / 2
+ + app.screenSpaceBounds.top * percent;
+ matrix.setScale(percent, percent);
+ matrix.postTranslate(dx, dy);
+ transaction.forSurface(app.leash)
+ .setAlpha(percent)
+ .setMatrix(matrix);
+ }
surfaceApplier.scheduleApply(transaction);
});
appAnimator.addListener(new AnimatorListenerAdapter() {
@@ -3962,15 +3971,9 @@
// Update flags to see if actions bar should show buttons for a single task or a pair of
// tasks.
mActionsView.updateForGroupedTask(isCurrentSplit);
- // Update flags to see if actions bar should show buttons for tablets or phones.
- mActionsView.updateForSmallScreen(!mActivity.getDeviceProfile().isTablet);
- // Update flags for 1p/3p launchers
- mActionsView.updateFor3pLauncher(!supportsAppPairs());
- if (enableDesktopWindowingMode()) {
- boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
- mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
- }
+ boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
+ mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
}
/** Returns if app pairs are supported in this launcher. Overridden in subclasses. */
@@ -4692,9 +4695,7 @@
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
- if (enableDesktopWindowingMode()) {
- updateDesktopTaskVisibility(false /* visible */);
- }
+ updateDesktopTaskVisibility(false /* visible */);
}
/**
@@ -4716,9 +4717,7 @@
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
- if (enableDesktopWindowingMode()) {
- updateDesktopTaskVisibility(false /* visible */);
- }
+ updateDesktopTaskVisibility(false /* visible */);
}
private void updateDesktopTaskVisibility(boolean visible) {
@@ -4922,9 +4921,7 @@
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
mSplitHiddenTaskView = null;
}
- if (enableDesktopWindowingMode()) {
- updateDesktopTaskVisibility(true /* visible */);
- }
+ updateDesktopTaskVisibility(true /* visible */);
}
private void safeRemoveDragLayerView(@Nullable View viewToRemove) {
@@ -5367,6 +5364,10 @@
finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
}
+ /**
+ * NOTE: Whatever value gets passed through to the toRecents param may need to also be set on
+ * {@link #mRecentsAnimationController#setWillFinishToHome}.
+ */
public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
@Nullable Runnable onFinishComplete) {
Log.d(TAG, "finishRecentsAnimation - mRecentsAnimationController: "
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 6a59ab4..a3e5a35 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,10 +16,12 @@
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 android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.content.Context;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -33,12 +35,14 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.app.animation.Interpolators;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
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.statemanager.StatefulActivity;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.util.SplitSelectStateController;
/**
@@ -51,6 +55,7 @@
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 StatefulActivity mLauncher;
public boolean mIsCurrentlyAnimating = false;
@@ -137,9 +142,24 @@
SplitSelectStateController splitSelectController =
((RecentsView) mLauncher.getOverviewPanel()).getSplitSelectController();
- splitSelectController.getSplitAnimationController().playPlaceholderDismissAnim(mLauncher,
- LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON);
- mLauncher.getStateManager().goToState(LauncherState.NORMAL);
+ StateManager stateManager = mLauncher.getStateManager();
+ BaseState startState = stateManager.getState();
+ long duration = startState.getTransitionDuration(mLauncher, 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(mLauncher,
+ LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON, duration);
+ stateAnim.play(dismissAnim);
+ stateManager.setCurrentAnimation(stateAnim, NORMAL);
+ stateAnim.start();
}
void ensureProperRotation() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 79bd107..cec0982 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -645,6 +645,7 @@
*/
public void bind(Task task, RecentsOrientedState orientedState) {
cancelPendingLoadTasks();
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "TaskView.bind: task=" + task);
mTask = task;
mTaskIdContainer[0] = mTask.key.id;
mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView, mIconView,
@@ -852,6 +853,8 @@
*/
@Nullable
public RunnableList launchTaskAnimated() {
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.launchTaskAnimated: mTask=" + mTask);
if (mTask != null) {
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
"TaskView.launchTaskAnimated: startActivityFromRecentsAsync");
@@ -902,6 +905,7 @@
* Starts the task associated with this view without any animation
*/
public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "TaskView.launchTask: mTask=" + mTask);
if (mTask != null) {
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
"TaskView.launchTask: startActivityFromRecentsAsync");
@@ -972,6 +976,9 @@
public RunnableList launchTasks() {
RecentsView recentsView = getRecentsView();
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.launchTasks: isRunningTask=" + isRunningTask() + ", "
+ + "remoteTargetHandles == null?" + (remoteTargetHandles == null));
if (isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
Log.e(TAG, "TaskView is not clickable as a live tile; returning to home.");
@@ -999,7 +1006,7 @@
// If the recents animation is cancelled somehow between the parent if block and
// here, try to launch the task as a non live tile task.
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
- "TaskView.java - launchTasks: recents animation is cancelled");
+ "TaskView.launchTasks: recents animation is cancelled");
RunnableList runnableList = launchTaskAnimated();
if (runnableList == null) {
Log.e(TAG, "Recents animation cancelled and cannot launch task as non-live tile"
@@ -1021,7 +1028,7 @@
public void onAnimationEnd(Animator animator) {
if (mTask != null && mTask.key.displayId != getRootViewDisplayId()) {
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
- "TaskView.java - launchTasks: onAnimationEnd");
+ "TaskView.launchTasks: onAnimationEnd");
launchTaskAnimated();
}
mIsClickableAsLiveTile = true;
@@ -1041,9 +1048,6 @@
recentsView.onTaskLaunchedInLiveTileMode();
return runnableList;
} else {
- testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
- "TaskView.java - launchTasks: isRunningTask=" + isRunningTask() + "||"
- + "remoteTargetHandles == null?" + (remoteTargetHandles == null));
return launchTaskAnimated();
}
}
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 c17aeaa..b478efa 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
@@ -16,6 +16,9 @@
package com.android.launcher3.taskbar.bubbles.animation
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
import android.content.Context
import android.graphics.Color
import android.graphics.Path
@@ -24,6 +27,8 @@
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.widget.FrameLayout
+import androidx.core.animation.AnimatorTestRule
+import androidx.core.animation.doOnEnd
import androidx.core.graphics.drawable.toBitmap
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.test.core.app.ApplicationProvider
@@ -39,10 +44,14 @@
import com.android.wm.shell.common.bubbles.BubbleInfo
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
import org.junit.Before
+import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@SmallTest
@@ -52,6 +61,10 @@
private val context = ApplicationProvider.getApplicationContext<Context>()
private val animatorScheduler = TestBubbleBarViewAnimatorScheduler()
+ companion object {
+ @JvmField @ClassRule val animatorTestRule = AnimatorTestRule()
+ }
+
@Before
fun setUp() {
PhysicsAnimatorTestUtils.prepareForTest()
@@ -86,6 +99,15 @@
val bubbleStashController = mock<BubbleStashController>()
whenever(bubbleStashController.isStashed).thenReturn(true)
+ val semaphore = Semaphore(0)
+ val hideHandleAnimator = AnimatorSet()
+ hideHandleAnimator.duration = 0
+ whenever(bubbleStashController.buildHideHandleAnimationForNewBubble())
+ .thenReturn(hideHandleAnimator)
+ // add an end listener to the hide handle animation. we add it when the animation starts
+ // to ensure that it gets called after all other end listeners.
+ hideHandleAnimator.doOnStart { hideHandleAnimator.doOnEnd { semaphore.release() } }
+
val animator =
BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler)
@@ -93,29 +115,44 @@
animator.animateBubbleInForStashed(bubble)
}
+ // wait for the stash handle animation to complete
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ // stash handle animation finished. verify that the stash handle is now hidden
+ verify(bubbleStashController).setStashAlpha(0f)
+
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(overflowView.visibility).isEqualTo(INVISIBLE)
assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
+ // wait for the show bubble animation to complete
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
DynamicAnimation.ALPHA,
- DynamicAnimation.TRANSLATION_Y
+ DynamicAnimation.TRANSLATION_Y,
+ DynamicAnimation.SCALE_Y,
)
assertThat(bubbleView.alpha).isEqualTo(1)
assertThat(bubbleView.translationY).isEqualTo(-50)
+ assertThat(bubbleView.scaleY).isEqualTo(1)
+ val showHandleAnimator = AnimatorSet()
+ showHandleAnimator.duration = 0
+ whenever(bubbleStashController.buildShowHandleAnimationForNewBubble())
+ .thenReturn(showHandleAnimator)
+ var showHandleAnimationStarted = false
+ showHandleAnimator.doOnStart { showHandleAnimationStarted = true }
+
+ // execute the hide bubble animation
assertThat(animatorScheduler.delayedBlock).isNotNull()
InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!)
+ // finish the hide bubble animation
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ animatorTestRule.advanceTimeBy(250)
+ }
- InstrumentationRegistry.getInstrumentation().waitForIdleSync()
-
- PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
- DynamicAnimation.ALPHA,
- DynamicAnimation.TRANSLATION_Y
- )
+ assertThat(showHandleAnimationStarted).isTrue()
assertThat(bubbleView.alpha).isEqualTo(1)
assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
@@ -125,6 +162,16 @@
assertThat(overflowView.visibility).isEqualTo(VISIBLE)
}
+ private fun AnimatorSet.doOnStart(onStart: () -> Unit) {
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animator: Animator) {
+ onStart()
+ }
+ }
+ )
+ }
+
private class TestBubbleBarViewAnimatorScheduler : BubbleBarViewAnimator.Scheduler {
var delayedBlock: (() -> Unit)? = null
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 18b1ea0..a7ed8a7 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -36,6 +36,7 @@
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.quickstep.RecentsModel
import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.util.SplitSelectStateController.SplitFromDesktopController
import com.android.systemui.shared.recents.model.Task
import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
import java.util.function.Consumer
@@ -65,6 +66,7 @@
private val context: StatefulActivity<*> = mock()
private val recentsModel: RecentsModel = mock()
private val pendingIntent: PendingIntent = mock()
+ private val splitFromDesktopController: SplitFromDesktopController = mock()
private lateinit var splitSelectStateController: SplitSelectStateController
@@ -607,6 +609,18 @@
assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
}
+ @Test
+ fun splitSelectStateControllerDestroyed_SplitFromDesktopControllerAlsoDestroyed() {
+ // Initiate split from desktop controller
+ splitSelectStateController.initSplitFromDesktopController(splitFromDesktopController)
+
+ // Simulate default controller being destroyed
+ splitSelectStateController.onDestroy()
+
+ // Verify desktop controller is also destroyed
+ verify(splitFromDesktopController).onDestroy()
+ }
+
// Generate GroupTask with default userId.
private fun generateGroupTask(
task1ComponentName: ComponentName,
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
new file mode 100644
index 0000000..5c7b4ab
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredictionsRequesterTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model
+
+import android.app.prediction.AppTarget
+import android.app.prediction.AppTargetEvent
+import android.app.prediction.AppTargetId
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Context
+import android.os.Process.myUserHandle
+import android.os.UserHandle
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.model.WidgetPredictionsRequester.buildBundleForPredictionSession
+import com.android.launcher3.model.WidgetPredictionsRequester.filterPredictions
+import com.android.launcher3.model.WidgetPredictionsRequester.notOnUiSurfaceFilter
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.PackageUserKey
+import com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Predicate
+import junit.framework.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidJUnit4::class)
+class WidgetsPredictionsRequesterTest {
+
+ private lateinit var mUserHandle: UserHandle
+ private lateinit var context: Context
+ private lateinit var deviceProfile: DeviceProfile
+ private lateinit var testInvariantProfile: InvariantDeviceProfile
+
+ private lateinit var widget1aInfo: AppWidgetProviderInfo
+ private lateinit var widget1bInfo: AppWidgetProviderInfo
+ private lateinit var widget2Info: AppWidgetProviderInfo
+
+ private lateinit var widgetItem1a: WidgetItem
+ private lateinit var widgetItem1b: WidgetItem
+ private lateinit var widgetItem2: WidgetItem
+
+ private lateinit var allWidgets: Map<PackageUserKey, List<WidgetItem>>
+
+ @Mock private lateinit var iconCache: IconCache
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mUserHandle = myUserHandle()
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ testInvariantProfile = LauncherAppState.getIDP(context)
+ deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
+
+ widget1aInfo =
+ createAppWidgetProviderInfo(
+ ComponentName.createRelative(APP_1_PACKAGE_NAME, APP_1_PROVIDER_A_CLASS_NAME)
+ )
+ widget1bInfo =
+ createAppWidgetProviderInfo(
+ ComponentName.createRelative(APP_1_PACKAGE_NAME, APP_1_PROVIDER_B_CLASS_NAME)
+ )
+ widgetItem1a = createWidgetItem(widget1aInfo)
+ widgetItem1b = createWidgetItem(widget1bInfo)
+
+ widget2Info =
+ createAppWidgetProviderInfo(
+ ComponentName.createRelative(APP_2_PACKAGE_NAME, APP_2_PROVIDER_1_CLASS_NAME)
+ )
+ widgetItem2 = createWidgetItem(widget2Info)
+
+ allWidgets =
+ mapOf(
+ PackageUserKey(APP_1_PACKAGE_NAME, mUserHandle) to
+ listOf(widgetItem1a, widgetItem1b),
+ PackageUserKey(APP_2_PACKAGE_NAME, mUserHandle) to listOf(widgetItem2),
+ )
+ }
+
+ @Test
+ fun buildBundleForPredictionSession_includesAddedAppWidgets() {
+ val existingWidgets = arrayListOf(widget1aInfo, widget1bInfo, widget2Info)
+
+ val bundle = buildBundleForPredictionSession(existingWidgets, TEST_UI_SURFACE)
+ val addedWidgetsBundleExtra =
+ bundle.getParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, AppTarget::class.java)
+
+ assertNotNull(addedWidgetsBundleExtra)
+ assertThat(addedWidgetsBundleExtra)
+ .containsExactly(
+ buildExpectedAppTargetEvent(
+ /*pkg=*/ APP_1_PACKAGE_NAME,
+ /*providerClassName=*/ APP_1_PROVIDER_A_CLASS_NAME,
+ /*user=*/ mUserHandle
+ ),
+ buildExpectedAppTargetEvent(
+ /*pkg=*/ APP_1_PACKAGE_NAME,
+ /*providerClassName=*/ APP_1_PROVIDER_B_CLASS_NAME,
+ /*user=*/ mUserHandle
+ ),
+ buildExpectedAppTargetEvent(
+ /*pkg=*/ APP_2_PACKAGE_NAME,
+ /*providerClassName=*/ APP_2_PROVIDER_1_CLASS_NAME,
+ /*user=*/ mUserHandle
+ )
+ )
+ }
+
+ @Test
+ fun filterPredictions_notOnUiSurfaceFilter_returnsOnlyEligiblePredictions() {
+ val widgetsAlreadyOnSurface = arrayListOf(widget1bInfo)
+ val filter: Predicate<WidgetItem> = notOnUiSurfaceFilter(widgetsAlreadyOnSurface)
+
+ val predictions =
+ listOf(
+ // already on surface
+ AppTarget(
+ AppTargetId(APP_1_PACKAGE_NAME),
+ APP_1_PACKAGE_NAME,
+ APP_1_PROVIDER_B_CLASS_NAME,
+ mUserHandle
+ ),
+ // eligible
+ AppTarget(
+ AppTargetId(APP_2_PACKAGE_NAME),
+ APP_2_PACKAGE_NAME,
+ APP_2_PROVIDER_1_CLASS_NAME,
+ mUserHandle
+ )
+ )
+
+ // only 2 was eligible
+ assertThat(filterPredictions(predictions, allWidgets, filter)).containsExactly(widgetItem2)
+ }
+
+ @Test
+ fun filterPredictions_appPredictions_returnsWidgetFromPackage() {
+ val widgetsAlreadyOnSurface = arrayListOf(widget1bInfo)
+ val filter: Predicate<WidgetItem> = notOnUiSurfaceFilter(widgetsAlreadyOnSurface)
+
+ val predictions =
+ listOf(
+ AppTarget(
+ AppTargetId(APP_1_PACKAGE_NAME),
+ APP_1_PACKAGE_NAME,
+ "$APP_1_PACKAGE_NAME.SomeActivity",
+ mUserHandle
+ ),
+ AppTarget(
+ AppTargetId(APP_2_PACKAGE_NAME),
+ APP_2_PACKAGE_NAME,
+ "$APP_2_PACKAGE_NAME.SomeActivity2",
+ mUserHandle
+ ),
+ )
+
+ assertThat(filterPredictions(predictions, allWidgets, filter))
+ .containsExactly(widgetItem1a, widgetItem2)
+ }
+
+ private fun createWidgetItem(
+ providerInfo: AppWidgetProviderInfo,
+ ): WidgetItem {
+ val widgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo)
+ return WidgetItem(widgetInfo, testInvariantProfile, iconCache, context)
+ }
+
+ companion object {
+ const val TEST_UI_SURFACE = "widgets_test"
+ const val BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets"
+
+ const val APP_1_PACKAGE_NAME = "com.example.app1"
+ const val APP_1_PROVIDER_A_CLASS_NAME = "app1Provider1"
+ const val APP_1_PROVIDER_B_CLASS_NAME = "app1Provider2"
+
+ const val APP_2_PACKAGE_NAME = "com.example.app2"
+ const val APP_2_PROVIDER_1_CLASS_NAME = "app2Provider1"
+
+ const val TEST_PACKAGE = "pkg"
+
+ private fun buildExpectedAppTargetEvent(
+ pkg: String,
+ providerClassName: String,
+ userHandle: UserHandle
+ ): AppTargetEvent {
+ val appTarget =
+ AppTarget.Builder(
+ /*id=*/ AppTargetId("widget:$pkg"),
+ /*packageName=*/ pkg,
+ /*user=*/ userHandle
+ )
+ .setClassName(providerClassName)
+ .build()
+ return AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_PIN)
+ .setLaunchLocation(TEST_UI_SURFACE)
+ .build()
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index df73e09..a9ff161 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
import android.graphics.Rect;
@@ -23,6 +25,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
@@ -45,6 +48,7 @@
@Test
@NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
Rect taskbarBoundsBefore = getTaskbar().getVisibleBounds();
// Go home and to an IME activity (any configuration change would do, as long as it
diff --git a/res/color-night-v31/material_color_surface_container_high.xml b/res/color-night-v31/material_color_surface_container_high.xml
index 002b88e..edd36fc 100644
--- a/res/color-night-v31/material_color_surface_container_high.xml
+++ b/res/color-night-v31/material_color_surface_container_high.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="12" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="17" />
</selector>
\ No newline at end of file
diff --git a/res/color-night-v31/material_color_surface_container_highest.xml b/res/color-night-v31/material_color_surface_container_highest.xml
index 002b88e..e54f953 100644
--- a/res/color-night-v31/material_color_surface_container_highest.xml
+++ b/res/color-night-v31/material_color_surface_container_highest.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="12" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="22" />
</selector>
\ No newline at end of file
diff --git a/res/color-night-v31/material_color_surface_container_low.xml b/res/color-night-v31/material_color_surface_container_low.xml
index 002b88e..40f0d4c 100644
--- a/res/color-night-v31/material_color_surface_container_low.xml
+++ b/res/color-night-v31/material_color_surface_container_low.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="12" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="10" />
</selector>
\ No newline at end of file
diff --git a/res/color-night-v31/material_color_surface_container_lowest.xml b/res/color-night-v31/material_color_surface_container_lowest.xml
index 002b88e..24f559b 100644
--- a/res/color-night-v31/material_color_surface_container_lowest.xml
+++ b/res/color-night-v31/material_color_surface_container_lowest.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="12" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="4" />
</selector>
\ No newline at end of file
diff --git a/res/color-v31/material_color_surface_container_high.xml b/res/color-v31/material_color_surface_container_high.xml
index b031c08..a996d51 100644
--- a/res/color-v31/material_color_surface_container_high.xml
+++ b/res/color-v31/material_color_surface_container_high.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="94" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="92" />
</selector>
\ No newline at end of file
diff --git a/res/color-v31/material_color_surface_container_highest.xml b/res/color-v31/material_color_surface_container_highest.xml
index b031c08..e7a535a 100644
--- a/res/color-v31/material_color_surface_container_highest.xml
+++ b/res/color-v31/material_color_surface_container_highest.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="94" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="90" />
</selector>
\ No newline at end of file
diff --git a/res/color-v31/material_color_surface_container_low.xml b/res/color-v31/material_color_surface_container_low.xml
index b031c08..b8fe01e 100644
--- a/res/color-v31/material_color_surface_container_low.xml
+++ b/res/color-v31/material_color_surface_container_low.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="94" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="96" />
</selector>
\ No newline at end of file
diff --git a/res/color-v31/material_color_surface_container_lowest.xml b/res/color-v31/material_color_surface_container_lowest.xml
index 674fc73..25e8666 100644
--- a/res/color-v31/material_color_surface_container_lowest.xml
+++ b/res/color-v31/material_color_surface_container_lowest.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="94" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="100" />
</selector>
\ No newline at end of file
diff --git a/res/drawable/bg_ps_header.xml b/res/drawable/bg_ps_header.xml
index 526bb5a..da31445 100644
--- a/res/drawable/bg_ps_header.xml
+++ b/res/drawable/bg_ps_header.xml
@@ -14,9 +14,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners android:radius="@dimen/ps_container_corner_radius" />
- <solid android:color="?attr/materialColorSurfaceContainerHigh" />
-</shape>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/accent_ripple_color">
+ <item>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/ps_container_corner_radius" />
+ <solid android:color="?attr/materialColorSurfaceContainerHigh" />
+ </shape>
+ </item>
+</ripple>
diff --git a/res/drawable/ps_lock_background.xml b/res/drawable/ps_lock_background.xml
index b81c23f..0be83db 100644
--- a/res/drawable/ps_lock_background.xml
+++ b/res/drawable/ps_lock_background.xml
@@ -15,13 +15,17 @@
~ limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="4dp">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/ps_lock_corner_radius" />
- <solid android:color="?attr/materialColorPrimaryFixedDim" />
- <padding
- android:left="@dimen/ps_lock_button_background_padding"
- android:right="@dimen/ps_lock_button_background_padding" />
- </shape>
-</inset>
\ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/accent_ripple_color">
+ <item>
+ <inset android:inset="4dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/ps_lock_corner_radius" />
+ <solid android:color="?attr/materialColorPrimaryFixedDim" />
+ <padding
+ android:left="@dimen/ps_lock_button_background_padding"
+ android:right="@dimen/ps_lock_button_background_padding" />
+ </shape>
+ </inset>
+ </item>
+</ripple>
diff --git a/res/layout/folder_app_pair.xml b/res/layout/folder_app_pair.xml
new file mode 100644
index 0000000..acecd46
--- /dev/null
+++ b/res/layout/folder_app_pair.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.launcher3.apppairs.AppPairIcon
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:focusable="true"
+ launcher:iconDisplay="folder" >
+ <com.android.launcher3.apppairs.AppPairIconGraphic
+ android:id="@+id/app_pair_icon_graphic"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="false" />
+ <com.android.launcher3.BubbleTextView
+ style="@style/BaseIcon"
+ android:id="@+id/app_pair_icon_name"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="false"
+ android:layout_gravity="top"
+ android:textColor="?attr/folderTextColor"
+ launcher:iconDisplay="folder" />
+</com.android.launcher3.apppairs.AppPairIcon>
\ No newline at end of file
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 106c5b7..12453a5 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -35,14 +35,6 @@
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:layout_gravity="fill"/>
-
- <ImageView
- android:id="@+id/widget_badge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:importantForAccessibility="no"
- android:layout_gravity="end|bottom"
- android:layout_margin="@dimen/profile_badge_margin"/>
</com.android.launcher3.widget.WidgetCellPreview>
<FrameLayout
@@ -51,44 +43,45 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
android:id="@+id/widget_text_container"
android:orientation="vertical">
<!-- The name of the widget. -->
- <TextView
- android:id="@+id/widget_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:fadingEdge="horizontal"
- android:layout_gravity="center_horizontal"
- android:gravity="center_horizontal|center_vertical"
- android:singleLine="true"
- android:maxLines="1"
- android:textColor="?android:attr/textColorPrimary"
- android:drawablePadding="@dimen/widget_cell_app_icon_padding"
- android:textSize="@dimen/widget_cell_font_size" />
-
- <!-- The original dimensions of the widget -->
<TextView
- android:id="@+id/widget_dims"
- android:layout_width="match_parent"
+ android:id="@+id/widget_name"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="@dimen/widget_cell_font_size"
- android:alpha="0.7" />
-
- <TextView
- android:id="@+id/widget_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textSize="@dimen/widget_cell_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="2"
android:ellipsize="end"
android:fadingEdge="horizontal"
- android:alpha="0.7" />
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal|center_vertical"
+ android:singleLine="true"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:drawablePadding="@dimen/widget_cell_app_icon_padding"
+ android:textSize="@dimen/widget_cell_font_size" />
+
+ <!-- The original dimensions of the widget -->
+ <TextView
+ android:id="@+id/widget_dims"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/widget_cell_font_size"
+ android:alpha="0.7" />
+
+ <TextView
+ android:id="@+id/widget_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textSize="@dimen/widget_cell_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:alpha="0.7" />
</LinearLayout>
<Button
@@ -97,8 +90,6 @@
android:layout_height="@dimen/widget_cell_add_button_height"
android:layout_gravity="center"
android:minWidth="0dp"
- android:paddingTop="@dimen/widget_cell_add_button_vertical_padding"
- android:paddingBottom="@dimen/widget_cell_add_button_vertical_padding"
android:paddingStart="@dimen/widget_cell_add_button_start_padding"
android:paddingEnd="@dimen/widget_cell_add_button_end_padding"
android:text="@string/widget_add_button_label"
@@ -106,7 +97,7 @@
android:textSize="@dimen/widget_cell_font_size"
android:gravity="center"
android:visibility="gone"
- android:drawableLeft="@drawable/ic_plus"
+ android:drawableStart="@drawable/ic_plus"
android:drawablePadding="8dp"
android:drawableTint="?attr/widgetPickerAddButtonTextColor"
android:background="@drawable/widget_cell_add_button_background" />
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index a19ea97..2121e40 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"አጣራ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"አልተሳካም፦ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"የግል ቦታ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ለማዋቀር ወይም ለመክፈት መታ ያድርጉ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"የግል"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"የግል ቦታ ቅንብሮች"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"የግል ቦታን ቆልፍ/ክፈት"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index dfd23da..30284b0 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -29,7 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"الشاشة الرئيسية"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"تقسيم الشاشة"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"معلومات تطبيق %1$s"</string>
- <string name="save_app_pair" msgid="5647523853662686243">"حفظ إعدادات ميزة \"استخدام تطبيقين في الوقت نفسه\""</string>
+ <string name="save_app_pair" msgid="5647523853662686243">"حفظ استخدام التطبيقين معًا"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"لا يمكن استخدام هذين التطبيقَين في الوقت نفسه على هذا الجهاز"</string>
<string name="app_pair_needs_unfold" msgid="4588897528143807002">"افتح الجهاز لاستخدام هذين التطبيقَين في الوقت نفسه"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فلتر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
<string name="private_space_label" msgid="2359721649407947001">"مساحة خاصة"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"النقر للإعداد أو الفتح"</string>
<string name="ps_container_title" msgid="4391796149519594205">"المساحة الخاصة"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"إعدادات المساحة الخاصة"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"قفل المساحة الخاصة أو فتح قفلها"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 898b9d6..f7775ab 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টাৰ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"বিফল: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"প্ৰাইভেট স্পে\'চ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ছেট আপ কৰিবলৈ টিপক অথবা খোলক"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ব্যক্তিগত"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ব্যক্তিগত স্পে’চৰ ছেটিং"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"প্ৰাইভেট স্পে\'চ লক/আনলক কৰক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index b714613..d9f4acd 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Alınmadı: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Şəxsi yer"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Toxunaraq ayarlayın və ya açın"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Şəxsi"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Şəxsi məkan ayarları"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Şəxsi məkanı kilidləyin/kiliddən çıxarın"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 6ff7970..2ae1c59 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -43,7 +43,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlozi"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovne aplikacije"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Neophodne aplikacije"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novosti i časopisi"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona za opuštanje"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste podesili ili otvorili"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Podešavanja privatnog prostora"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključaj/otključaj privatni prostor"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 22642d7..c5147ed 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не ўдалося: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Прыватная вобласць"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Націсніце, каб наладзіць або адкрыць"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Прыватная"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Налады прыватнай вобласці"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заблакіраваць (разблакіраваць) прыватную вобласць"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index a9e4a26..ae94d14 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтър"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Лично пространство"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Докоснете за настройване или отваряне"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Лично"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Настройки за личното пространство"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заключване/отключване на личното пространство"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index f20416b..d25b9c5 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টার"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ব্যক্তিগত স্পেস"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"সেট-আপ করতে বা খুলতে ট্যাপ করুন"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ব্যক্তিগত"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ব্যক্তিগত স্পেসের সেটিংস"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ব্যক্তিগত স্পেস লক/আনলক করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 3e2ed4d..42365c1 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatan prostor"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste postavili ili otvorili"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključavanje/otključavanje privatnog prostora"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 4ffd4bb..933084f 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -118,7 +118,7 @@
<string name="app_pair_name_format" msgid="8134106404716224054">"Parella d\'aplicacions: <xliff:g id="APP1">%1$s</xliff:g> i <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Estil i fons de pantalla"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"Edita la pantalla d\'inici"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Config. pantalla d\'inici"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configuració de la pantalla d\'inici"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
<string name="allow_rotation_title" msgid="7222049633713050106">"Permet la rotació de la pantalla d\'inici"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espai privat"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Toca per configurar o obrir"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Configuració d\'Espai privat"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloqueja o desbloqueja Espai privat"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 61521ae..267f40b 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Soukromý prostor"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Klepnutím nastavíte nebo otevřete"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Soukromé"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Nastavení soukromého prostoru"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zamknout/odemknout soukromý prostor"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index f85a2a9..65347e0 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Tryk for at konfigurere eller åbne"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Indstillinger for privat rum"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås/oplås det private område"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 04e42b2..838ccc7 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Φίλτρο"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Ιδιωτικός χώρος"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Πάτημα για ρύθμιση ή άνοιγμα"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Ιδιωτικό"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Ρυθμίσεις Ιδιωτικού χώρου"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Κλείδωμα/Ξεκλείδωμα Ιδιωτικού χώρου"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index c377990..02335ab 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espacio privado"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Presiona para configurar o abrir"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Configuración de Espacio privado"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear o desbloquear Espacio privado"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5c950b1..9e494ad 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espacio privado"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Toca para configurarlo o abrirlo"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Ajustes del espacio privado"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/Desbloquear espacio privado"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 610c243..4cbf751 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nurjus: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privaatne ruum"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Seadistamiseks või avamiseks puudutage"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privaatne"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Privaatse ruumi seaded"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privaatse ruumi lukustamine/avamine"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 1d25061..71ef150 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Iragazi"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Huts egin du: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Eremu pribatua"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Sakatu konfiguratzeko edo irekitzeko"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Pribatua"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Eremu pribatuaren ezarpenak"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blokeatu/Desblokeatu eremu pribatua"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index dc48d82..bf9263b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فیلتر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ناموفق بود: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"فضای خصوصی"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"برای راهاندازی یا باز کردن، ضربه بزنید"</string>
<string name="ps_container_title" msgid="4391796149519594205">"خصوصی"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"تنظیمات «فضای خصوصی»"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"قفل/ باز کردن «فضای خصوصی»"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 820f7eb..0e8573d 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Suodatin"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Epäonnistui: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Yksityinen tila"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Ota käyttöön tai avaa napauttamalla"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Yksityinen"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Yksityisen tilan asetukset"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lukitse yksityinen tila / avaa sen lukitus"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 54ad858..627e996 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -123,8 +123,8 @@
<string name="allow_rotation_title" msgid="7222049633713050106">"Autoriser la rotation de l\'écran d\'accueil"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activé"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivé"</string>
+ <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activées"</string>
+ <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivées"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Accès aux notifications requis"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les pastilles de notification, activez les notifications de l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espace privé"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Appuyer pour ouvrir ou configurer"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privé"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Paramètres d\'Espace privé"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Verrouiller/Déverrouiller Espace privé"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 0a7c96c..e330b05 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Erro: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espazo privado"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Toca para configuralo ou abrilo"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privado"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Configuración do espazo privado"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear ou desbloquear o espazo privado"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index c3be263..912d798 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ફિલ્ટર કરો"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"નિષ્ફળ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ખાનગી સ્પેસ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"સેટઅપ કરવા કે ખોલવા માટે ટૅપ કરો"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ખાનગી"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ખાનગી સ્પેસના સેટિંગ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ખાનગી સ્પેસને લૉક/અનલૉક કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 7b6a9f9..0c4da56 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फ़िल्टर"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"पूरा नहीं हुआ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"प्राइवेट स्पेस"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"सेट अप करने या खोलने के लिए टैप करें"</string>
<string name="ps_container_title" msgid="4391796149519594205">"निजी"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"प्राइवेट स्पेस सेटिंग"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"प्राइवेट स्पेस को लॉक करें/अनलॉक करें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 89f95f0..b754e95 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privatni prostor"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Dodirnite da biste postavili ili otvorili"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privatno"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Postavke privatnog prostora"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zaključavanje/otključavanje privatnog prostora"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index bdb473b..e1aaad2 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -188,13 +188,12 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Զտեք"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Չհաջողվեց կատարել գործողությունը (<xliff:g id="WHAT">%1$s</xliff:g>)"</string>
<string name="private_space_label" msgid="2359721649407947001">"Անձնական տարածք"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Հպեք կարգավորելու կամ բացելու համար"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Անձնական"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Անձնական տարածքի կարգավորումներ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Կողպել/ապակողպել անձնական տարածքը"</string>
<string name="ps_container_lock_title" msgid="2640257399982364682">"Կողպում"</string>
- <string name="ps_container_transition" msgid="8667331812048014412">"Անցում անձնական տարածք"</string>
+ <string name="ps_container_transition" msgid="8667331812048014412">"Անցում մասնավոր տարածք"</string>
<string name="ps_add_button_label" msgid="8611055839242385935">"Հավելվածների տեղադրում"</string>
<string name="ps_add_button_content_description" msgid="3254274107740952556">"Հավելվածների տեղադրում անձնական տարածքում"</string>
<string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Լրացուցիչ ընտրացանկ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index b80f233..d0b0964 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Ruang pribadi"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Ketuk untuk menyiapkan atau membuka"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Pribadi"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Setelan Ruang Pribadi"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kunci/Buka Kunci Ruang Pribadi"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 1287322..506d68e 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Sía"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mistókst: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Einkarými"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Ýttu til að setja upp eða opna"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Lokað"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Stillingar einkarýmis"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Læsaeinkarými/taka einkarými úr lás"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 4805748..ed122fe 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Operazione non riuscita: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Spazio privato"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Tocca per configurare o aprire"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privato"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Impostazioni dello Spazio privato"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blocca/sblocca Spazio privato"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 2f10ef3..b25150a 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"סינון"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"הפעולה נכשלה: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"מרחב פרטי"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"אפשר להקיש כדי להגדיר או לפתוח"</string>
<string name="ps_container_title" msgid="4391796149519594205">"פרטי"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"הגדרות המרחב הפרטי"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"נעילה או ביטול הנעילה של המרחב הפרטי"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 3112a55..b8977be 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"フィルタ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失敗: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"プライベート スペース"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"設定したり開いたりするにはタップしてください"</string>
<string name="ps_container_title" msgid="4391796149519594205">"プライベート"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"プライベート スペースの設定"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"プライベート スペースをロック / ロック解除する"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index ea5c207..6f7bd9b 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ფილტრი"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"პირადი სივრცე"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"დასაყენებლად ან გასახსნელად შეეხეთ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"პირადი"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"პირადი სივრცის პარამეტრები"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"პირადი სივრცის ჩაკეტვა/განბლოკვა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 56895cb..4156253 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Сүзгі"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Жеке бөлме"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Реттеу немесе ашу үшін түртіңіз"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Жеке"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Жеке бөлме параметрлері"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке бөлмені құлыптау/оның құлпын ашу"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index f955b54..3aca213 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"តម្រង"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"បានបរាជ័យ៖ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"បន្ទប់ឯកជន"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ចុចដើម្បីរៀបចំ ឬបើក"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ឯកជន"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ការកំណត់ Private Space"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ចាក់សោ/ដោះសោ Private Space"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 3fd334a..93c63ff 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ಫಿಲ್ಟರ್"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ವಿಫಲವಾಗಿದೆ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ಸೆಟಪ್ ಮಾಡಲು ಅಥವಾ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ಖಾಸಗಿ"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಲಾಕ್/ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 4814ede..5457674 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"필터"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"실패: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"비공개 스페이스"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"탭하여 설정 또는 열기"</string>
<string name="ps_container_title" msgid="4391796149519594205">"비공개"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"비공개 스페이스 설정"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"비공개 스페이스 잠금/잠금 해제"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 8d59747..a0a605e 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -187,11 +187,11 @@
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Улантуу"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Чыпкалоо"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
- <string name="private_space_label" msgid="2359721649407947001">"Жеке чөйрө"</string>
+ <string name="private_space_label" msgid="2359721649407947001">"Жеке мейкиндик"</string>
<string name="private_space_secondary_label" msgid="9203933341714508907">"Тууралоо же ачуу үчүн таптап коюңуз"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Жеке"</string>
- <string name="ps_container_settings" msgid="6059734123353320479">"Жеке чөйрөнүн параметрлери"</string>
- <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке чөйрөнү кулпулоо/кулпусун ачуу"</string>
+ <string name="ps_container_settings" msgid="6059734123353320479">"Жеке мейкиндиктин параметрлери"</string>
+ <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Жеке мейкиндикти кулпулоо/кулпусун ачуу"</string>
<string name="ps_container_lock_title" msgid="2640257399982364682">"Кулпулоо"</string>
<string name="ps_container_transition" msgid="8667331812048014412">"Жеке чөйрөгө өтүү"</string>
<string name="ps_add_button_label" msgid="8611055839242385935">"Колдонмолорду орнотуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 5c0d6a8..e3142da 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ກັ່ນຕອງ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ບໍ່ສຳເລັດ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ພື້ນທີ່ສ່ວນຕົວ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ແຕະເພື່ອຕັ້ງຄ່າ ຫຼື ເປີດ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ສ່ວນຕົວ"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ການຕັ້ງຄ່າພື້ນທີ່ສ່ວນຕົວ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ລັອກ/ປົດລັອກພື້ນທີ່ສ່ວນຕົວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index abcbb25..f0573a9 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruoti"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nepavyko: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privati erdvė"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Palieskite, kad nustatytumėte arba atidarytumėte"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privatus"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Privačios erdvės nustatymai"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Užrakinti ir (arba) atrakinti privačią erdvę"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index b400b03..8a4ab47 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrs"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Neizdevās: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privātā telpa"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Pieskarieties, lai iestatītu vai atvērtu"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privātā mape"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Privātās mapes iestatījumi"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloķēt/atbloķēt privāto mapi"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 6e09073..051ab2e 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -188,14 +188,13 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Приватен простор"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Допрете за да поставите или отворите"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Приватен"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Поставки за „Приватен простор“"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заклучување/отклучување на „Приватен простор“"</string>
<string name="ps_container_lock_title" msgid="2640257399982364682">"Брава"</string>
<string name="ps_container_transition" msgid="8667331812048014412">"Префрлање на „Приватен простор“"</string>
- <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталирање апликации"</string>
+ <string name="ps_add_button_label" msgid="8611055839242385935">"Инсталирајте апликации"</string>
<string name="ps_add_button_content_description" msgid="3254274107740952556">"Инсталирање апликации во „Приватен простор“"</string>
<string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Проширено балонче"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 26bd635..ce5ccc6 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ഫിൽട്ടർ ചെയ്യുക"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"പരാജയപ്പെട്ടു: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"സ്വകാര്യ സ്പേസ്"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"സജ്ജീകരിക്കാനോ തുറക്കാനോ ടാപ്പ് ചെയ്യുക"</string>
<string name="ps_container_title" msgid="4391796149519594205">"സ്വകാര്യം"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"സ്വകാര്യ സ്പേസ് ക്രമീകരണം"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"സ്വകാര്യ സ്പേസ് ലോക്ക് ചെയ്യുക/അൺലോക്ക് ചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 5e86120..254947a 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Шүүлтүүр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Амжилтгүй болсон: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Хувийн орон зай"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Тохируулах эсвэл нээхийн тулд товших"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Хувийн"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Private Space-н тохиргоо"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Private Space-г түгжих/түгжээг тайлах"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 9ec42c1..9e524ac 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"हे करता आले नाही: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"खाजगी स्पेस"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"सेट करण्यासाठी किंवा उघडण्यासाठी टॅप करा"</string>
<string name="ps_container_title" msgid="4391796149519594205">"खाजगी"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"खाजगी स्पेस ची सेटिंग्ज"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"खाजगी स्पेस लॉक/अनलॉक करा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 63f500a..d2a8732 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Tapis"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Ruang privasi"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Ketik untuk menyediakan atau membuka"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Peribadi"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Tetapan Ruang Peribadi"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kunci/Buka kunci Ruang Peribadi"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 93b61f8..d32e6f1 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"မအောင်မြင်ပါ− <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"သီးသန့်ချတ်ခန်း"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"စနစ်ထည့်သွင်းရန် (သို့) ဖွင့်ရန် တို့ပါ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"သီးသန့်"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"သီးသန့်ချတ်ခန်း ဆက်တင်များ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"သီးသန့်ချတ်ခန်း လော့ခ်ချ/ဖွင့်ရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 4a352f5..22002e8 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislyktes: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Trykk for å konfigurere eller åpne"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Innstillinger for Private Space"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås / lås opp Private Space"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 0f5e1a5..15bc8e4 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"निजी स्पेस"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"सेटअप गर्न वा खोल्न ट्याप गर्नुहोस्"</string>
<string name="ps_container_title" msgid="4391796149519594205">"निजी"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"निजी स्पेससम्बन्धी सेटिङ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"निजी स्पेस लक/अनलक गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index ace1d8e..b2e106c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -177,7 +177,7 @@
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Werk-apps hebben badges en zijn zichtbaar voor je IT-beheerder"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="3040901117349444598">"Werk-apps zijn onderbroken"</string>
+ <string name="work_apps_paused_title" msgid="3040901117349444598">"Werk-apps zijn gepauzeerd"</string>
<string name="work_apps_paused_info_body" msgid="1687828929959237477">"Je krijgt geen meldingen van je werk-apps"</string>
<string name="work_apps_paused_body" msgid="261634750995824906">"Je werk-apps kunnen je geen meldingen sturen, je batterij niet gebruiken en geen toegang krijgen tot je locatie"</string>
<string name="work_apps_paused_telephony_unavailable_body" msgid="8358872357502756790">"Je krijgt geen telefoongesprekken, tekstberichten of meldingen van je werk-apps"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filteren"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislukt: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privéruimte"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Tik om in te stellen of te openen"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privé"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Instellingen voor privéruimte"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Privéruimte vergrendelen/ontgrendelen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 2c03967..bdd39e1 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ଫିଲ୍ଟର୍"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ସେଟ ଅପ କରିବା କିମ୍ବା ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ପ୍ରାଇଭେଟ"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ପ୍ରାଇଭେଟ ସ୍ପେସ ସେଟିଂସ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଲକ/ଅନଲକ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 321d6c0..2b89ebf 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ਫਿਲਟਰ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ਇਹ ਕਾਰਵਾਈ ਅਸਫਲ ਹੋਈ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ਨਿੱਜੀ ਸਪੇਸ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"ਸੈੱਟਅੱਪ ਕਰਨ ਜਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ਨਿੱਜੀ"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ਨਿੱਜੀ ਸਪੇਸ ਸੰਬੰਧੀ ਸੈਟਿੰਗਾਂ"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਲਾਕ/ਅਣਲਾਕ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 7cc4f1f..2c7908c 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruj"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Niepowodzenie: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Obszar prywatny"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Kliknij, aby skonfigurować lub otworzyć"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Prywatne"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Ustawienia obszaru prywatnego"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Zablokuj/odblokuj obszar prywatny"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 07fe3db..2e10d80 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Falha: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Espaço particular"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Toque para configurar ou abrir"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Particular"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Configurações do Espaço particular"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Bloquear/desbloquear o Espaço particular"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 30b2ea6..81f7250 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtru"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Eșuare: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Spațiu privat"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Atinge pentru a configura sau a deschide"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Setări spațiu privat"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Blochează / deblochează spațiul privat"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index a96e2e1..9c35b9d 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Фильтр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
<string name="private_space_label" msgid="2359721649407947001">"Личное пространство"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Нажмите, чтобы настроить или открыть"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Доступно только вам"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Настройки личного пространства"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Блокировка и разблокировка личного пространства"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 53aee23..506c634 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"පෙරහන"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"අසාර්ථකයි: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"පෞද්ගලික ඉඩ"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"පිහිටුවීමට හෝ විවෘත කිරීමට තට්ටු කරන්න"</string>
<string name="ps_container_title" msgid="4391796149519594205">"පෞද්ගලික"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"පෞද්ගලික අවකාශ සැකසීම්"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"පෞද්ගලික අවකාශය අගුළු දමන්න/අගුළු හරින්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e058442..58e280c 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrujte"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Súkromný priestor"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Klepnutím nastavte alebo otvorte"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Súkromné"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Nastavenia súkromného priestoru"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Súkromný priestor zamykania a odomykania"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 062a5e1..fe3650a 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Dështoi: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Hapësira private"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Trokit për të konfiguruar ose për të hapur"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Private"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Cilësimet e \"Hapësirës private\""</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Kyç/Shkyç \"Hapësirën private\""</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 6b3a1ea..5bae891 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -43,7 +43,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетни екран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основне апликације"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходне апликације"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и часописи"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона за опуштање"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Није успело: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Приватни простор"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Додирните да бисте подесили или отворили"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Приватно"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Подешавања приватног простора"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Закључај/откључај приватни простор"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 94a9ff0..5bec2a9 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Misslyckades: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Privat rum"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Tryck för att ställa in eller öppna"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Inställningar för privat rum"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås eller lås upp ditt privata rum"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 069894b..f0c28cd 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Kichujio"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Hitilafu: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Nafasi ya faragha"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Gusa ili uweke mipangilio au ufungue"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Faragha"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Mipangilio ya Nafasi ya Faragha"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Funga/Fungua Nafasi ya Faragha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 043b654..edca769 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"வடிப்பான்"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"தோல்வி: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"தனிப்பட்ட சேமிப்பிடம்"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"அமைக்கவோ திறக்கவோ தட்டுங்கள்"</string>
<string name="ps_container_title" msgid="4391796149519594205">"தனிப்பட்டது"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"தனிப்பட்ட சேமிப்பிட அமைப்புகள்"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"தனிப்பட்ட சேமிப்பிடத்தை லாக்/அன்லாக் செய்யும்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index ef1cc44..20649ad 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ఫిల్టర్ చేయి"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"విఫలమైంది: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"ప్రైవేట్ స్పేస్"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"సెటప్ చేయడానికి లేదా తెరవడానికి ట్యాప్ చేయండి"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ప్రైవేట్"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"ప్రైవేట్ స్పేస్ సెట్టింగ్లు"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ప్రైవేట్ స్పేస్ను లాక్/అన్లాక్ చేయండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 37e2c21..05b3910 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"ตัวกรอง"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ไม่สำเร็จ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"พื้นที่ส่วนตัว"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"แตะเพื่อตั้งค่าหรือเปิด"</string>
<string name="ps_container_title" msgid="4391796149519594205">"ส่วนตัว"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"การตั้งค่าพื้นที่ส่วนตัว"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"ล็อก/ปลดล็อกพื้นที่ส่วนตัว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 33dbecf..0ea7e16 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -29,7 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Impormasyon ng app para sa %1$s"</string>
- <string name="save_app_pair" msgid="5647523853662686243">"I-save ang pares ng app"</string>
+ <string name="save_app_pair" msgid="5647523853662686243">"I-save ang app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Hindi sinusuportahan sa device na ito ang pares ng app na ito"</string>
<string name="app_pair_needs_unfold" msgid="4588897528143807002">"I-unfold ang device para magamit ang pares ng app na ito"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Hindi nagawa: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Pribadong space"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"I-tap para i-set up o buksan"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Pribado"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Mga Setting ng Pribadong Space"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"I-lock/I-unlock ang Pribadong Space"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index f299b2c..d509343 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Başarısız: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Gizli alan"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Kurmak veya açmak için dokunun"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Gizli"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Gizli Alan Ayarları"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Gizli Alanı Kilitleyin/Kilidini Açın"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index c1703f3..7f586ba 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не вдалося <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Приватний простір"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Натисніть, щоб налаштувати або відкрити"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Приватні"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Налаштування приватного простору"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Заблокувати/розблокувати приватний простір"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 3169e03..57cb87f 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فلٹر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ناکام ہو گيا: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"نجی اسپیس"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"سیٹ اپ کرنے یا کھولنے کے لیے تھپتھپائیں"</string>
<string name="ps_container_title" msgid="4391796149519594205">"نجی"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"نجی اسپیس کی ترتیبات"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"نجی اسپیس کو مقفل کریں/غیر مقفل کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index b80f4cd..eacf845 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Saralash"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Shaxsiy xona"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Sozlash yoki ochish uchun bosing"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Yopiq"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Shaxsiy xona sozlamalari"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Shaxsiy xonani ochish/qulflash"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index a3e26d0..5e76aed 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Bộ lọc"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Không thực hiện được thao tác: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Không gian riêng tư"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Nhấn để thiết lập hoặc mở"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Riêng tư"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Cài đặt không gian riêng tư"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Khoá/mở khoá không gian riêng tư"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index c8e524d..3b1d0c7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -29,10 +29,10 @@
<string name="home_screen" msgid="5629429142036709174">"主屏幕"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分屏"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的应用信息"</string>
- <string name="save_app_pair" msgid="5647523853662686243">"保存应用对"</string>
+ <string name="save_app_pair" msgid="5647523853662686243">"保存应用组合"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"在该设备上无法使用此应用对"</string>
- <string name="app_pair_needs_unfold" msgid="4588897528143807002">"展开设备即可使用此应用对"</string>
+ <string name="app_pair_needs_unfold" msgid="4588897528143807002">"展开设备即可使用此应用组合"</string>
<string name="long_press_widget_to_add" msgid="3587712543577675817">"轻触并按住即可移动微件。"</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"点按两次并按住微件即可移动该微件或使用自定义操作。"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"过滤器"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失败:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"私密空间"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"点按即可设置或打开"</string>
<string name="ps_container_title" msgid="4391796149519594205">"私密"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"私密空间设置"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"锁定/解锁私密空间"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 46669eb..da8b547 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"操作失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"私人空間"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"輕按即可設定或開啟"</string>
<string name="ps_container_title" msgid="4391796149519594205">"私人"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"「私人空間」設定"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"鎖定/解鎖「私人空間」"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8fddcc4..6a7313f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"私人空間"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"輕觸即可設定或開啟"</string>
<string name="ps_container_title" msgid="4391796149519594205">"私人"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"私人空間設定"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"鎖定/取消鎖定私人空間"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 308e6e0..3db9e5d 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -188,8 +188,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Hlunga"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Yehlulekile: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
<string name="private_space_label" msgid="2359721649407947001">"Isikhala esiyimfihlo"</string>
- <!-- no translation found for private_space_secondary_label (9203933341714508907) -->
- <skip />
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"Thepha ukuze usethe noma uvule"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Okuyimfihlo"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Amasethingi Esikhala Esiyimfihlo"</string>
<string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Khiya/Vula Isikhala Esiyimfihlo"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index ffc8bd0..b3a25c0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -52,6 +52,7 @@
<attr name="folderIconBorderColor" format="color" />
<attr name="folderTextColor" format="color" />
<attr name="folderHintTextColor" format="color" />
+ <attr name="appPairSurfaceInFolder" format="color" />
<attr name="isFolderDarkText" format="boolean" />
<attr name="workspaceAccentColor" format="color" />
<attr name="workspaceSurfaceColor" format="color" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8c58931..e9f8f38 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -183,7 +183,6 @@
<dimen name="widget_cell_add_button_height">48dp</dimen>
<dimen name="widget_cell_add_button_start_padding">8dp</dimen>
<dimen name="widget_cell_add_button_end_padding">16dp</dimen>
- <dimen name="widget_cell_add_button_vertical_padding">10dp</dimen>
<dimen name="widget_tabs_button_horizontal_padding">4dp</dimen>
<dimen name="widget_tabs_horizontal_padding">16dp</dimen>
@@ -277,7 +276,6 @@
<!-- Sizes for managed profile badges -->
<dimen name="profile_badge_size">24dp</dimen>
- <dimen name="profile_badge_margin">5dp</dimen>
<dimen name="profile_badge_minimum_top">2dp</dimen>
<!-- Shadows and outlines -->
@@ -498,6 +496,7 @@
<dimen name="ps_button_width">40dp</dimen>
<dimen name="ps_lock_button_width">89dp</dimen>
<dimen name="ps_app_divider_padding">16dp</dimen>
+ <dimen name="ps_extra_bottom_padding">16dp</dimen>
<dimen name="ps_lock_corner_radius">20dp</dimen>
<dimen name="ps_lock_icon_size">20dp</dimen>
<dimen name="ps_lock_icon_margin_top">10dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e35d241..00b962e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -61,6 +61,7 @@
<item name="isFolderDarkText">true</item>
<item name="folderTextColor">@color/folder_text_color_light</item>
<item name="folderHintTextColor">@color/folder_hint_text_color_light</item>
+ <item name="appPairSurfaceInFolder">@color/material_color_surface_container_lowest</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
<item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
@@ -172,6 +173,7 @@
<item name="isFolderDarkText">false</item>
<item name="folderTextColor">@color/folder_text_color_dark</item>
<item name="folderHintTextColor">@color/folder_hint_text_color_dark</item>
+ <item name="appPairSurfaceInFolder">@color/material_color_surface_container_lowest</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
<item name="iconOnlyShortcutColor">#B3FFFFFF</item>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 4a277f0..af3fdcc 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -200,6 +200,10 @@
}
public static void showForWidget(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
+ // If widget is not added to view hierarchy, we cannot show resize frame at correct location
+ if (widget.getParent() == null) {
+ return;
+ }
Launcher launcher = Launcher.getLauncher(cellLayout.getContext());
AbstractFloatingView.closeAllOpenViews(launcher);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 4c8ed15..83236d1 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -912,7 +912,7 @@
@Nullable
public PreloadIconDrawable applyProgressLevel() {
if (!(getTag() instanceof ItemInfoWithIcon)
- || !((ItemInfoWithIcon) getTag()).isActiveArchive()) {
+ || ((ItemInfoWithIcon) getTag()).isInactiveArchive()) {
return null;
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 2e0f676..bc36336 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -359,6 +359,15 @@
return displayOption.grid.name;
}
+ /**
+ * @deprecated This is a temporary solution because on the backup and restore case we modify the
+ * IDP, this resets it. b/332974074
+ */
+ @Deprecated
+ public void reset(Context context) {
+ initGrid(context, getCurrentGridName(context));
+ }
+
@VisibleForTesting
public static String getDefaultGridName(Context context) {
return new InvariantDeviceProfile().initGrid(context, null);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index cfa8967..3273f27 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -680,7 +680,7 @@
@Override
public void onBackCancelled() {
- mStateManager.getState().onBackCancelled(Launcher.this);
+ Launcher.this.onBackCancelled();
}
};
}
@@ -2086,6 +2086,10 @@
mStateManager.getState().onBackInvoked(this);
}
+ protected void onBackCancelled() {
+ mStateManager.getState().onBackCancelled(this);
+ }
+
protected void onScreenOnChanged(boolean isOn) {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 27e084c..cb19b14 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -20,7 +20,6 @@
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.util.Log
-import android.view.ViewConfiguration
import androidx.annotation.VisibleForTesting
import com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN
import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
@@ -319,7 +318,7 @@
val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
nonRestorableItem(
"LPNH_TIMEOUT_MS",
- ViewConfiguration.getLongPressTimeout(),
+ 450,
EncryptionType.MOVE_TO_DEVICE_PROTECTED
)
@JvmField
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index ce3c55a..0fc3211 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -16,8 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
@@ -73,6 +74,7 @@
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.celllayout.CellInfo;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper;
@@ -241,6 +243,8 @@
public static final int REORDER_TIMEOUT = 650;
protected final Alarm mReorderAlarm = new Alarm();
private PreviewBackground mFolderCreateBg;
+ /** The underlying view that we are dragging something over. */
+ private View mDragOverView = null;
private FolderIcon mDragOverFolderIcon = null;
private boolean mCreateUserFolderOnDrop = false;
private boolean mAddToExistingFolderOnDrop = false;
@@ -1873,12 +1877,9 @@
return false;
}
- boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) dropOverView.getTag()).container
- != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
- boolean willBecomeShortcut =
- (info.itemType == ITEM_TYPE_APPLICATION ||
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
+ boolean aboveShortcut = Folder.willAccept(dropOverView.getTag())
+ && ((ItemInfo) dropOverView.getTag()).container != CONTAINER_HOTSEAT_PREDICTION;
+ boolean willBecomeShortcut = Folder.willAcceptItemType(info.itemType);
return (aboveShortcut && willBecomeShortcut);
}
@@ -1925,12 +1926,12 @@
mCreateUserFolderOnDrop = false;
final int screenId = getCellLayoutId(target);
- boolean aboveShortcut = (v.getTag() instanceof WorkspaceItemInfo);
- boolean willBecomeShortcut = (newView.getTag() instanceof WorkspaceItemInfo);
+ boolean aboveShortcut = Folder.willAccept(v.getTag());
+ boolean willBecomeShortcut = Folder.willAccept(newView.getTag());
if (aboveShortcut && willBecomeShortcut) {
- WorkspaceItemInfo sourceInfo = (WorkspaceItemInfo) newView.getTag();
- WorkspaceItemInfo destInfo = (WorkspaceItemInfo) v.getTag();
+ ItemInfo sourceInfo = (ItemInfo) newView.getTag();
+ ItemInfo destInfo = (ItemInfo) v.getTag();
// if the drag started here, we need to remove it from the workspace
if (!external) {
getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
@@ -2384,6 +2385,11 @@
if (mFolderCreateBg != null) {
mFolderCreateBg.animateToRest();
}
+
+ if (mDragOverView instanceof AppPairIcon api) {
+ api.getIconDrawableArea().onTemporaryContainerChange(null);
+ mDragOverView = null;
+ }
}
private void cleanupAddToFolder() {
@@ -2659,32 +2665,36 @@
return;
}
- final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
+ mDragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
ItemInfo info = dragObject.dragInfo;
- boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
+ boolean userFolderPending = willCreateUserFolder(info, mDragOverView, false);
if (mDragMode == DRAG_MODE_NONE && userFolderPending) {
mFolderCreateBg = new PreviewBackground();
mFolderCreateBg.setup(mLauncher, mLauncher, null,
- dragOverView.getMeasuredWidth(), dragOverView.getPaddingTop());
+ mDragOverView.getMeasuredWidth(), mDragOverView.getPaddingTop());
// The full preview background should appear behind the icon
mFolderCreateBg.isClipping = false;
+ if (mDragOverView instanceof AppPairIcon api) {
+ api.getIconDrawableArea().onTemporaryContainerChange(DISPLAY_FOLDER);
+ }
+
mFolderCreateBg.animateToAccept(mDragTargetLayout, mTargetCell[0], mTargetCell[1]);
mDragTargetLayout.clearDragOutlines();
setDragMode(DRAG_MODE_CREATE_FOLDER);
if (dragObject.stateAnnouncer != null) {
dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
- .getDescriptionForDropOver(dragOverView, getContext()));
+ .getDescriptionForDropOver(mDragOverView, getContext()));
}
return;
}
- boolean willAddToFolder = willAddToExistingUserFolder(info, dragOverView);
+ boolean willAddToFolder = willAddToExistingUserFolder(info, mDragOverView);
if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
- mDragOverFolderIcon = ((FolderIcon) dragOverView);
+ mDragOverFolderIcon = ((FolderIcon) mDragOverView);
mDragOverFolderIcon.onDragEnter(info);
if (mDragTargetLayout != null) {
mDragTargetLayout.clearDragOutlines();
@@ -2693,7 +2703,7 @@
if (dragObject.stateAnnouncer != null) {
dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
- .getDescriptionForDropOver(dragOverView, getContext()));
+ .getDescriptionForDropOver(mDragOverView, getContext()));
}
return;
}
@@ -3314,7 +3324,7 @@
}
} else if (child instanceof FolderIcon) {
FolderInfo folderInfo = (FolderInfo) info;
- List<WorkspaceItemInfo> matches = folderInfo.getContents().stream()
+ List<ItemInfo> matches = folderInfo.getContents().stream()
.filter(matcher)
.collect(Collectors.toList());
if (!matches.isEmpty()) {
@@ -3381,7 +3391,7 @@
FolderInfo fi = (FolderInfo) info;
if (fi.anyMatch(matcher)) {
FolderDotInfo folderDotInfo = new FolderDotInfo();
- for (WorkspaceItemInfo si : fi.getContents()) {
+ for (ItemInfo si : fi.getContents()) {
folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
}
((FolderIcon) v).setDotInfo(folderDotInfo);
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 52073cc..84d6a6f 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -23,6 +23,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.BaseAccessibilityDelegate.DragType;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -117,7 +118,7 @@
return mContext.getString(R.string.item_moved);
} else {
ItemInfo info = (ItemInfo) child.getTag();
- if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+ if (Folder.willAccept(info)) {
return mContext.getString(R.string.folder_created);
} else if (info instanceof FolderInfo) {
@@ -148,8 +149,8 @@
if (TextUtils.isEmpty(info.title)) {
// Find the first item in the folder.
FolderInfo folder = (FolderInfo) info;
- WorkspaceItemInfo firstItem = null;
- for (WorkspaceItemInfo shortcut : folder.getContents()) {
+ ItemInfo firstItem = null;
+ for (ItemInfo shortcut : folder.getContents()) {
if (firstItem == null || firstItem.rank > shortcut.rank) {
firstItem = shortcut;
}
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 4b65b73..c255eb5 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.Flags.enableExpandingPauseWorkButton;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY;
@@ -85,6 +86,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.recyclerview.AllAppsRecyclerViewPool;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -96,6 +98,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -156,6 +159,7 @@
};
private final Paint mNavBarScrimPaint;
private final int mHeaderProtectionColor;
+ private final int mPrivateSpaceBottomExtraSpace;
private final Path mTmpPath = new Path();
private final RectF mTmpRectF = new RectF();
protected AllAppsPagedView mViewPager;
@@ -218,6 +222,8 @@
this,
mActivityContext.getStatsLogManager(),
UserCache.INSTANCE.get(mActivityContext));
+ mPrivateSpaceBottomExtraSpace = context.getResources().getDimensionPixelSize(
+ R.dimen.ps_extra_bottom_padding);
mAH = Arrays.asList(null, null, null);
mNavBarScrimPaint = new Paint();
mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext));
@@ -511,7 +517,7 @@
switchToTab(ActivityAllAppsContainerView.AdapterHolder.MAIN);
// Scroll to bottom
if (mPrivateProfileManager != null) {
- mPrivateProfileManager.scrollForViewToBeVisibleInContainer(
+ mPrivateProfileManager.scrollForHeaderToBeVisibleInContainer(
getActiveAppsRecyclerView(),
getPersonalAppList().getAdapterItems(),
mPrivateProfileManager.getPsHeaderHeight(),
@@ -1366,6 +1372,18 @@
invalidateHeader();
}
+ /**
+ * Set {@link Animator.AnimatorListener} on {@link mAllAppsTransitionController} to observe
+ * animation of backing out of all apps search view to all apps view.
+ */
+ public void setAllAppsSearchBackAnimatorListener(Animator.AnimatorListener listener) {
+ Preconditions.assertNotNull(mAllAppsTransitionController);
+ if (mAllAppsTransitionController == null) {
+ return;
+ }
+ mAllAppsTransitionController.setAllAppsSearchBackAnimationListener(listener);
+ }
+
public void setScrimView(ScrimView scrimView) {
mScrimView = scrimView;
}
@@ -1564,6 +1582,14 @@
int bottomOffset = 0;
if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
+ } else if (isMain() && mPrivateProfileManager != null) {
+ Optional<AdapterItem> privateSpaceHeaderItem = mAppsList.getAdapterItems()
+ .stream()
+ .filter(item -> item.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER)
+ .findFirst();
+ if (privateSpaceHeaderItem.isPresent()) {
+ bottomOffset = mPrivateSpaceBottomExtraSpace;
+ }
}
if (isSearchBarFloating()) {
bottomOffset += mSearchContainer.getHeight();
@@ -1580,5 +1606,9 @@
private boolean isSearch() {
return mType == SEARCH;
}
+
+ private boolean isMain() {
+ return mType == MAIN;
+ }
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 63f6227..a4d1dc1 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -44,6 +44,7 @@
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
@@ -167,6 +168,8 @@
private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
private final int mNavScrimFlag;
+ @Nullable private Animator.AnimatorListener mAllAppsSearchBackAnimationListener;
+
private boolean mIsVerticalLayout;
// Animation in this class is controlled by a single variable {@link mProgress}.
@@ -312,11 +315,25 @@
}
}
- /** Animate all apps view to 1f scale. */
+ /** Set {@link Animator.AnimatorListener} for scaling all apps scale to 1 animation. */
+ public void setAllAppsSearchBackAnimationListener(Animator.AnimatorListener listener) {
+ mAllAppsSearchBackAnimationListener = listener;
+ }
+
+ /**
+ * Animate all apps view to 1f scale. This is called when backing (exiting) from all apps
+ * search view to all apps view.
+ */
public void animateAllAppsToNoScale() {
- mAllAppScale.animateToValue(1f)
- .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
- .start();
+ if (mAllAppScale.isAnimating()) {
+ return;
+ }
+ Animator animator = mAllAppScale.animateToValue(1f)
+ .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS);
+ if (mAllAppsSearchBackAnimationListener != null) {
+ animator.addListener(mAllAppsSearchBackAnimationListener);
+ }
+ animator.start();
}
/**
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 2190e1a..3ba1eed 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_LEFT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_TOP_RIGHT;
import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED;
+import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED;
import android.content.Context;
import android.view.LayoutInflater;
@@ -40,7 +41,6 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.views.ActivityContext;
@@ -262,6 +262,17 @@
icon.reset();
icon.applyFromApplicationInfo(adapterItem.itemInfo);
icon.setOnFocusChangeListener(mIconFocusListener);
+ PrivateProfileManager privateProfileManager = mApps.getPrivateProfileManager();
+ // Set the alpha of the private space icon to 0 upon expanding the header so the
+ // alpha can animate -> 1.
+ if (icon.getAlpha() == 0 || icon.getAlpha() == 1) {
+ icon.setAlpha(privateProfileManager != null
+ && privateProfileManager.isPrivateSpaceItem(adapterItem)
+ && privateProfileManager.getAnimationScrolling()
+ && privateProfileManager.getAnimate()
+ && privateProfileManager.getCurrentState() == STATE_ENABLED
+ ? 0 : 1);
+ }
break;
}
case VIEW_TYPE_EMPTY_SEARCH: {
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index eb967bc..be120cc 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -40,6 +40,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
@@ -52,6 +53,7 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearSmoothScroller;
@@ -91,13 +93,27 @@
private static final int SETTINGS_OPACITY_DURATION = 160;
private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<UserHandle> mPrivateProfileMatcher;
+ private final int mPsHeaderHeight;
+ private final RecyclerView.OnScrollListener mOnIdleScrollListener =
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ mAnimationScrolling = false;
+ }
+ }
+ };
private Set<String> mPreInstalledSystemPackages = new HashSet<>();
private Intent mAppInstallerIntent = new Intent();
private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator;
private boolean mPrivateSpaceSettingsAvailable;
private boolean mIsAnimationRunning;
- private int mHeaderHeight;
private boolean mAnimate;
+ private boolean mAnimationScrolling;
+ private Runnable mOnPSHeaderAdded;
+ @Nullable
+ private RelativeLayout mPSHeader;
public PrivateProfileManager(UserManager userManager,
ActivityAllAppsContainerView<?> allApps,
@@ -107,6 +123,8 @@
mAllApps = allApps;
mPrivateProfileMatcher = (user) -> userCache.getUserInfo(user).isPrivate();
UI_HELPER_EXECUTOR.post(this::initializeInBackgroundThread);
+ mPsHeaderHeight = mAllApps.getContext().getResources().getDimensionPixelSize(
+ R.dimen.ps_header_height);
}
/** Adds Private Space Header to the layout. */
@@ -172,19 +190,26 @@
.get(mAllApps.mActivityContext).getValue(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, 0);
}
- /** Resets the current state of Private Profile, w.r.t. to Launcher. */
+ /**
+ * Resets the current state of Private Profile, w.r.t. to Launcher. The decorator should only
+ * be applied upon expand before animating. When collapsing, reset() will remove the decorator
+ * when animation is not running.
+ */
public void reset() {
int previousState = getCurrentState();
boolean isEnabled = !mAllApps.getAppsStore()
.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
setCurrentState(updatedState);
- resetPrivateSpaceDecorator(updatedState);
+ if (mPSHeader != null) {
+ mPSHeader.setAlpha(1);
+ }
if (transitioningFromLockedToUnlocked(previousState, updatedState)) {
postUnlock();
} else if (transitioningFromUnlockedToLocked(previousState, updatedState)){
executeLock();
}
+ resetPrivateSpaceDecorator(updatedState);
}
/** Opens the Private Space Settings Page. */
@@ -263,7 +288,7 @@
mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
} else {
// Remove Private Space Decorator from the Recycler view.
- if (mPrivateAppsSectionDecorator != null) {
+ if (mPrivateAppsSectionDecorator != null && !mIsAnimationRunning) {
mainAdapterHolder.mRecyclerView.removeItemDecoration(mPrivateAppsSectionDecorator);
}
}
@@ -289,10 +314,13 @@
/** Collapses the private space before the app list has been updated. */
void executeLock() {
- MAIN_EXECUTOR.execute(this::collapsePrivateSpace);
+ MAIN_EXECUTOR.execute(() -> updatePrivateStateAnimator(false));
}
void setAnimationRunning(boolean isAnimationRunning) {
+ if (!isAnimationRunning) {
+ mAnimate = false;
+ }
mIsAnimationRunning = isAnimationRunning;
}
@@ -325,8 +353,13 @@
/** Add Private Space Header view elements based upon {@link UserProfileState} */
public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
+ mPSHeader = parent;
+ if (mOnPSHeaderAdded != null) {
+ MAIN_EXECUTOR.execute(mOnPSHeaderAdded);
+ mOnPSHeaderAdded = null;
+ }
// Set the transition duration for the settings and lock button to animate.
- ViewGroup settingAndLockGroup = parent.findViewById(R.id.settingsAndLockGroup);
+ ViewGroup settingAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
if (mAnimate) {
enableLayoutTransition(settingAndLockGroup);
} else {
@@ -335,16 +368,15 @@
}
//Add quietMode image and action for lock/unlock button
- ViewGroup lockButton =
- parent.findViewById(R.id.ps_lock_unlock_button);
+ ViewGroup lockButton = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
assert lockButton != null;
addLockButton(lockButton);
//Trigger lock/unlock action from header.
- addHeaderOnClickListener(parent);
+ addHeaderOnClickListener(mPSHeader);
//Add image and action for private space settings button
- ImageButton settingsButton = parent.findViewById(R.id.ps_settings_button);
+ ImageButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
assert settingsButton != null;
addPrivateSpaceSettingsButton(settingsButton);
@@ -352,7 +384,6 @@
ImageView transitionView = parent.findViewById(R.id.ps_transition_image);
assert transitionView != null;
addTransitionImage(transitionView);
- mHeaderHeight = parent.getHeight();
}
/**
@@ -379,8 +410,10 @@
private void addHeaderOnClickListener(RelativeLayout header) {
if (getCurrentState() == STATE_DISABLED) {
header.setOnClickListener(view -> lockingAction(/* lock */ false));
+ header.setClickable(true);
} else {
header.setOnClickListener(null);
+ header.setClickable(false);
}
}
@@ -436,16 +469,18 @@
smoothScroller.setTargetPosition(i);
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
- layoutManager.startSmoothScroll(smoothScroller);
+ startAnimationScroll(allAppsRecyclerView, layoutManager, smoothScroller);
+ currentItem.decorationInfo = null;
}
break;
}
// Make the private space apps gone to "collapse".
- if (currentItem.decorationInfo != null) {
+ if (isPrivateSpaceItem(currentItem)) {
RecyclerView.ViewHolder viewHolder =
allAppsRecyclerView.findViewHolderForAdapterPosition(i);
if (viewHolder != null) {
viewHolder.itemView.setVisibility(GONE);
+ currentItem.decorationInfo = null;
}
}
}
@@ -455,7 +490,7 @@
* Upon expanding, only scroll to the item position in the adapter that allows the header to be
* visible.
*/
- public int scrollForViewToBeVisibleInContainer(
+ public int scrollForHeaderToBeVisibleInContainer(
AllAppsRecyclerView allAppsRecyclerView,
List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems,
int psHeaderHeight,
@@ -495,7 +530,7 @@
smoothScroller.setTargetPosition(itemToScrollTo);
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
- layoutManager.startSmoothScroll(smoothScroller);
+ startAnimationScroll(allAppsRecyclerView, layoutManager, smoothScroller);
}
}
return itemToScrollTo;
@@ -531,27 +566,56 @@
return collapseAnim;
}
+ private ValueAnimator animateAlphaOfIcons(boolean isExpanding) {
+ float from = isExpanding ? 0 : 1;
+ float to = isExpanding ? 1 : 0;
+ AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+ List<BaseAllAppsAdapter.AdapterItem> allAppsAdapterItems =
+ mAllApps.getActiveRecyclerView().getApps().getAdapterItems();
+ ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+ alphaAnim.setDuration(EXPAND_COLLAPSE_DURATION);
+ alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float newAlpha = (float) valueAnimator.getAnimatedValue();
+ for (int i = 0; i < allAppsAdapterItems.size(); i++) {
+ BaseAllAppsAdapter.AdapterItem currentItem = allAppsAdapterItems.get(i);
+ if (isPrivateSpaceItem(currentItem) &&
+ currentItem.viewType != VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+ RecyclerView.ViewHolder viewHolder =
+ allAppsRecyclerView.findViewHolderForAdapterPosition(i);
+ if (viewHolder != null) {
+ viewHolder.itemView.setAlpha(newAlpha);
+ }
+ }
+ }
+ }
+ });
+ return alphaAnim;
+ }
+
/**
* Using PropertySetter{@link PropertySetter}, we can update the view's attributes within an
* animation. At the moment, collapsing, setting alpha changes, and animating the text is done
* here.
*/
- private void updatePrivateStateAnimator(boolean expand, @Nullable ViewGroup psHeader) {
- if (psHeader == null) {
+ private void updatePrivateStateAnimator(boolean expand) {
+ if (mPSHeader == null) {
+ mOnPSHeaderAdded = () -> updatePrivateStateAnimator(expand);
+ setAnimationRunning(false);
return;
}
- ViewGroup settingsAndLockGroup = psHeader.findViewById(R.id.settingsAndLockGroup);
- ViewGroup lockButton = psHeader.findViewById(R.id.ps_lock_unlock_button);
+ ViewGroup settingsAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
+ ViewGroup lockButton = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
if (settingsAndLockGroup.getLayoutTransition() == null) {
// Set a new transition if the current ViewGroup does not already contain one as each
// transition should only happen once when applied.
enableLayoutTransition(settingsAndLockGroup);
}
-
- PropertySetter setter = new AnimatedPropertySetter();
- ImageButton settingsButton = psHeader.findViewById(R.id.ps_settings_button);
- updateSettingsGearAlpha(settingsButton, expand, setter);
- AnimatorSet animatorSet = setter.buildAnim();
+ PropertySetter headerSetter = new AnimatedPropertySetter();
+ ImageButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
+ updateSettingsGearAlpha(settingsButton, expand, headerSetter);
+ AnimatorSet animatorSet = headerSetter.buildAnim();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -562,22 +626,50 @@
});
animatorSet.addListener(forEndCallback(() -> {
setAnimationRunning(false);
- mAnimate = false;
if (!expand) {
// Call onAppsUpdated() because it may be canceled when this animation occurs.
mAllApps.getPersonalAppList().onAppsUpdated();
+ if (isPrivateSpaceHidden()) {
+ // TODO (b/325455879): Figure out if we can avoid this.
+ mAllApps.getActiveRecyclerView().getAdapter().notifyDataSetChanged();
+ }
}
}));
- // Play the collapsing together of the stateAnimator to avoid being unable to scroll to the
- // header. Otherwise the smooth scrolling will scroll higher when played with the state
- // animator.
- if (!expand) {
- animatorSet.playTogether(animateCollapseAnimation());
+ if (expand) {
+ animatorSet.playTogether(animateAlphaOfIcons(true));
+ } else {
+ if (isPrivateSpaceHidden()) {
+ animatorSet.playSequentially(animateAlphaOfIcons(false),
+ animateCollapseAnimation(), fadeOutHeaderAlpha());
+ } else {
+ animatorSet.playSequentially(animateAlphaOfIcons(false),
+ animateCollapseAnimation());
+ }
}
animatorSet.setDuration(EXPAND_COLLAPSE_DURATION);
animatorSet.start();
}
+ /** Fades out the private space container. */
+ private ValueAnimator fadeOutHeaderAlpha() {
+ if (mPSHeader == null) {
+ return new ValueAnimator();
+ }
+ float from = 1;
+ float to = 0;
+ ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+ alphaAnim.setDuration(EXPAND_COLLAPSE_DURATION);
+ alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ if (mPSHeader != null) {
+ mPSHeader.setAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ }
+ });
+ return alphaAnim;
+ }
+
/** Animates the layout changes when the text of the button becomes visible/gone. */
private void enableLayoutTransition(ViewGroup settingsAndLockGroup) {
LayoutTransition settingsAndLockTransition = new LayoutTransition();
@@ -617,10 +709,9 @@
// Animate the text and settings icon.
DeviceProfile deviceProfile =
ActivityContext.lookupContext(mAllApps.getContext()).getDeviceProfile();
- scrollForViewToBeVisibleInContainer(mainAdapterHolder.mRecyclerView, adapterItems,
+ scrollForHeaderToBeVisibleInContainer(mainAdapterHolder.mRecyclerView, adapterItems,
getPsHeaderHeight(), deviceProfile.allAppsCellHeightPx);
- ViewGroup psHeader = getPsHeader(mainAdapterHolder.mRecyclerView, adapterItems);
- updatePrivateStateAnimator(true, psHeader);
+ updatePrivateStateAnimator(true);
}
}
@@ -635,36 +726,28 @@
});
}
- private void collapsePrivateSpace() {
- AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
- AlphabeticalAppsList<?> appList = allAppsRecyclerView.getApps();
- if (appList == null) {
- return;
- }
- ViewGroup psHeader = getPsHeader(allAppsRecyclerView, appList.getAdapterItems());
- assert psHeader != null;
- updatePrivateStateAnimator(false, psHeader);
+ /** Starts the smooth scroll with the provided smoothScroller and add idle listener. */
+ private void startAnimationScroll(AllAppsRecyclerView allAppsRecyclerView,
+ RecyclerView.LayoutManager layoutManager, RecyclerView.SmoothScroller smoothScroller) {
+ mAnimationScrolling = true;
+ layoutManager.startSmoothScroll(smoothScroller);
+ allAppsRecyclerView.removeOnScrollListener(mOnIdleScrollListener);
+ allAppsRecyclerView.addOnScrollListener(mOnIdleScrollListener);
+ }
+
+ boolean getAnimate() {
+ return mAnimate;
+ }
+
+ boolean getAnimationScrolling() {
+ return mAnimationScrolling;
}
int getPsHeaderHeight() {
- return mHeaderHeight;
+ return mPsHeaderHeight;
}
- /** Get the private space header from the adapter items. */
- @Nullable
- private ViewGroup getPsHeader(AllAppsRecyclerView allAppsRecyclerView,
- List<BaseAllAppsAdapter.AdapterItem> adapterItems){
- ViewGroup psHeader = null;
- for (int i = 0; i < adapterItems.size(); i++) {
- BaseAllAppsAdapter.AdapterItem currentItem = adapterItems.get(i);
- if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
- RecyclerView.ViewHolder viewHolder =
- allAppsRecyclerView.findViewHolderForAdapterPosition(i);
- if (viewHolder != null) {
- psHeader = (ViewGroup) viewHolder.itemView;
- }
- }
- }
- return psHeader;
+ boolean isPrivateSpaceItem(BaseAllAppsAdapter.AdapterItem item) {
+ return item.decorationInfo != null;
}
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 9010f82..8e82d89 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -30,11 +30,13 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Reorderable;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.AppPairInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.views.ActivityContext;
@@ -179,10 +181,22 @@
return mIconGraphic;
}
+ public int getContainer() {
+ return mContainer;
+ }
+
+ /**
+ * Ensures that both app icons in the pair are loaded in high resolution.
+ */
+ public void verifyHighRes() {
+ IconCache iconCache = LauncherAppState.getInstance(getContext()).getIconCache();
+ getInfo().fetchHiResIconsIfNeeded(iconCache);
+ }
+
/**
* Called when WorkspaceItemInfos get updated, and the app pair icon may need to be redrawn.
*/
- public void maybeRedrawForWorkspaceUpdate(Predicate<WorkspaceItemInfo> itemCheck) {
+ public void maybeRedrawForWorkspaceUpdate(Predicate<ItemInfo> itemCheck) {
// If either of the app pair icons return true on the predicate (i.e. in the list of
// updated apps), redraw the icon graphic (icon background and both icons).
if (getInfo().anyMatch(itemCheck)) {
diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawable.java b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
index c0ac11a..db83d91 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
+++ b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
@@ -32,7 +32,7 @@
* A composed Drawable consisting of the two app pair icons and the background behind them (looks
* like two rectangles).
*/
-class AppPairIconDrawable extends Drawable {
+public class AppPairIconDrawable extends Drawable {
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final AppPairIconDrawingParams mP;
private final FastBitmapDrawable mIcon1;
@@ -102,6 +102,7 @@
}
mIcon2.draw(canvas);
+ canvas.restore();
}
/**
@@ -205,4 +206,14 @@
public void setColorFilter(ColorFilter colorFilter) {
mBackgroundPaint.setColorFilter(colorFilter);
}
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mP.getIconSize();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mP.getIconSize();
+ }
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
index 62e5771..45dc013 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
@@ -20,6 +20,7 @@
import com.android.launcher3.BubbleTextView.DISPLAY_FOLDER
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
+import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
class AppPairIconDrawingParams(val context: Context, container: Int) {
@@ -62,7 +63,7 @@
// The app pair icon appears differently in portrait and landscape.
var isLeftRightSplit: Boolean = true
// The background paint color (based on container).
- val bgColor: Int
+ var bgColor: Int = 0
init {
val activity: ActivityContext = ActivityContext.lookupContext(context)
@@ -77,22 +78,21 @@
innerPadding = iconSize * INNER_PADDING_SCALE
memberIconSize = iconSize * MEMBER_ICON_SCALE
updateOrientation(dp)
- if (container == DISPLAY_FOLDER) {
- val ta =
- context.theme.obtainStyledAttributes(
- intArrayOf(R.attr.materialColorSurfaceContainerLowest)
- )
- bgColor = ta.getColor(0, 0)
- ta.recycle()
- } else {
- val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
- bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
- ta.recycle()
- }
+ updateBgColor(container)
}
/** Checks the device orientation and updates isLeftRightSplit accordingly. */
fun updateOrientation(dp: DeviceProfile) {
isLeftRightSplit = dp.isLeftRightSplit
}
+
+ fun updateBgColor(container: Int) {
+ if (container == DISPLAY_FOLDER) {
+ bgColor = Themes.getAttrColor(context, R.attr.appPairSurfaceInFolder)
+ } else {
+ val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
+ bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
+ ta.recycle()
+ }
+ }
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index a3a1cfc..ed593ae 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
-import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.Gravity
import android.widget.FrameLayout
@@ -52,7 +51,10 @@
* 2) One of the member apps can't be launched due to screen size requirements.
*/
@JvmStatic
- fun composeDrawable(appPairInfo: AppPairInfo, p: AppPairIconDrawingParams): Drawable {
+ fun composeDrawable(
+ appPairInfo: AppPairInfo,
+ p: AppPairIconDrawingParams
+ ): AppPairIconDrawable {
// Generate new icons, using themed flag if needed.
val flags = if (Themes.isThemedIconEnabled(p.context)) BitmapInfo.FLAG_THEMED else 0
val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, flags)
@@ -81,7 +83,7 @@
private lateinit var parentIcon: AppPairIcon
private lateinit var drawParams: AppPairIconDrawingParams
- private lateinit var drawable: Drawable
+ lateinit var drawable: AppPairIconDrawable
fun init(icon: AppPairIcon, container: Int) {
parentIcon = icon
@@ -116,10 +118,13 @@
redraw()
}
- /** Updates the icon drawable and redraws it */
- fun redraw() {
- drawable = composeDrawable(parentIcon.info, drawParams)
- invalidate()
+ /**
+ * When the icon is temporary moved to a different colored surface, update the background color.
+ * Calling this method with [null] reverts the icon back to its default color.
+ */
+ fun onTemporaryContainerChange(newContainer: Int?) {
+ drawParams.updateBgColor(newContainer ?: parentIcon.container)
+ redraw()
}
/**
@@ -136,6 +141,12 @@
)
}
+ /** Updates the icon drawable and redraws it */
+ fun redraw() {
+ drawable = composeDrawable(parentIcon.info, drawParams)
+ invalidate()
+ }
+
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
drawable.draw(canvas)
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1c34c72..e476138 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -33,7 +33,6 @@
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
import android.content.res.Resources;
-import android.view.ViewConfiguration;
import androidx.annotation.VisibleForTesting;
@@ -132,8 +131,17 @@
"Allow entering All Apps from Overview (e.g. long swipe up from app)");
public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS =
- getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", DISABLED,
- "Add dev options to customize the LPNH trigger slop and milliseconds");
+ getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", ENABLED,
+ "Add dev options and server side control to customize the LPNH "
+ + "trigger slop and milliseconds");
+
+ public static final BooleanFlag CUSTOM_LPH_THRESHOLDS = getReleaseFlag(331800576,
+ "CUSTOM_LPH_THRESHOLDS", DISABLED,
+ "Server side control to customize LPH timeout and touch slop");
+
+ public static final BooleanFlag OVERRIDE_LPNH_LPH_THRESHOLDS = getReleaseFlag(331799727,
+ "OVERRIDE_LPNH_LPH_THRESHOLDS", DISABLED,
+ "Enable AGSA override for LPNH and LPH timeout and touch slop");
public static final BooleanFlag ANIMATE_LPNH =
getReleaseFlag(308693847, "ANIMATE_LPNH", TEAMFOOD,
@@ -155,8 +163,7 @@
LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP);
public static final IntFlag LPNH_TIMEOUT_MS =
- FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS",
- ViewConfiguration.getLongPressTimeout(),
+ FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS", 450,
"Controls lpnh timeout in milliseconds", LONG_PRESS_NAV_HANDLE_TIMEOUT_MS);
public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index aa3c5ba..dcc55e6 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -19,6 +19,9 @@
import static android.text.TextUtils.isEmpty;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
@@ -66,14 +69,12 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Alarm;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
@@ -166,6 +167,22 @@
private static final Rect sTempRect = new Rect();
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
+ /**
+ * Checks if {@code o} is an {@link ItemInfo} type that can be placed in folders.
+ */
+ public static boolean willAccept(Object o) {
+ return o instanceof ItemInfo info && willAcceptItemType(info.itemType);
+ }
+
+ /**
+ * Checks if {@code itemType} is a type that can be placed in folders.
+ */
+ public static boolean willAcceptItemType(int itemType) {
+ return itemType == ITEM_TYPE_APPLICATION
+ || itemType == ITEM_TYPE_DEEP_SHORTCUT
+ || itemType == ITEM_TYPE_APP_PAIR;
+ }
+
private final Alarm mReorderAlarm = new Alarm(Looper.getMainLooper());
private final Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper());
private final Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper());
@@ -313,9 +330,7 @@
public boolean startDrag(View v, DragOptions options) {
Object tag = v.getTag();
- if (tag instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
-
+ if (tag instanceof ItemInfo item) {
mEmptyCellRank = item.rank;
mCurrentDragView = v;
@@ -346,14 +361,12 @@
}
mContent.removeItem(mCurrentDragView);
- if (dragObject.dragInfo instanceof WorkspaceItemInfo) {
- mItemsInvalidated = true;
+ mItemsInvalidated = true;
- // We do not want to get events for the item being removed, as they will get handled
- // when the drop completes
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.remove((WorkspaceItemInfo) dragObject.dragInfo, true);
- }
+ // We do not want to get events for the item being removed, as they will get handled
+ // when the drop completes
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.remove(dragObject.dragInfo, true);
}
mDragInProgress = true;
mItemAddedBackToSelfViaIcon = false;
@@ -493,7 +506,7 @@
mInfo = info;
mFromTitle = info.title;
mFromLabelState = info.getFromLabelState();
- ArrayList<WorkspaceItemInfo> children = info.getContents();
+ ArrayList<ItemInfo> children = info.getContents();
Collections.sort(children, ITEM_POS_COMPARATOR);
updateItemLocationsInDatabaseBatch(true);
@@ -626,7 +639,7 @@
// onDropComplete. Perform cleanup once drag-n-drop ends.
mDragController.addDragListener(this);
- ArrayList<WorkspaceItemInfo> items = new ArrayList<>(mInfo.getContents());
+ ArrayList<ItemInfo> items = new ArrayList<>(mInfo.getContents());
mEmptyCellRank = items.size();
items.add(null); // Add an empty spot at the end
@@ -647,7 +660,7 @@
* is animated relative to the specified View. If the View is null, no animation
* is played.
*/
- private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
+ private void animateOpen(List<ItemInfo> items, int pageNo) {
if (items == null || items.size() <= 1) {
Log.d(TAG, "Couldn't animate folder open because items is: " + items);
return;
@@ -896,8 +909,7 @@
public boolean acceptDrop(DragObject d) {
final ItemInfo item = d.dragInfo;
final int itemType = item.itemType;
- return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
+ return Folder.willAcceptItemType(itemType);
}
public void onDragEnter(DragObject d) {
@@ -1050,7 +1062,7 @@
}
} else {
// The drag failed, we need to return the item to the folder
- WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
+ ItemInfo info = d.dragInfo;
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
ArrayList<View> views = getIconsInReadingOrder();
@@ -1099,7 +1111,7 @@
ArrayList<ItemInfo> items = new ArrayList<>();
int total = mInfo.getContents().size();
for (int i = 0; i < total; i++) {
- WorkspaceItemInfo itemInfo = mInfo.getContents().get(i);
+ ItemInfo itemInfo = mInfo.getContents().get(i);
if (verifier.updateRankAndPos(itemInfo, i)) {
items.add(itemInfo);
}
@@ -1112,8 +1124,7 @@
Executors.MODEL_EXECUTOR.post(() -> {
FolderNameInfos nameInfos = new FolderNameInfos();
FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
- fnp.getSuggestedFolderName(
- getContext(), mInfo.getContents(), nameInfos);
+ fnp.getSuggestedFolderName(getContext(), mInfo.getAppContents(), nameInfos);
mInfo.suggestedFolderNames = nameInfos;
});
}
@@ -1298,15 +1309,15 @@
d.deferDragViewCleanupPostAnimation = false;
mRearrangeOnClose = true;
} else {
- final WorkspaceItemInfo si;
+ final ItemInfo si;
if (pasiSi != null) {
si = pasiSi;
} else if (d.dragInfo instanceof WorkspaceItemFactory) {
// Came from all apps -- make a copy.
si = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(launcher);
} else {
- // WorkspaceItemInfo
- si = (WorkspaceItemInfo) d.dragInfo;
+ // WorkspaceItemInfo or AppPairInfo
+ si = d.dragInfo;
}
View currentDragView;
@@ -1314,7 +1325,7 @@
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
// Actually move the item in the database if it was an external drag. Call this
- // before creating the view, so that WorkspaceItemInfo is updated appropriately.
+ // before creating the view, so that the ItemInfo is updated appropriately.
mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(
si, mInfo.id, 0, si.cellX, si.cellY);
mIsExternalDrag = false;
@@ -1376,14 +1387,14 @@
// This is used so the item doesn't immediately appear in the folder when added. In one case
// we need to create the illusion that the item isn't added back to the folder yet, to
// to correspond to the animation of the icon back into the folder. This is
- public void hideItem(WorkspaceItemInfo info) {
+ public void hideItem(ItemInfo info) {
View v = getViewForInfo(info);
if (v != null) {
v.setVisibility(INVISIBLE);
}
}
- public void showItem(WorkspaceItemInfo info) {
+ public void showItem(ItemInfo info) {
View v = getViewForInfo(info);
if (v != null) {
v.setVisibility(VISIBLE);
@@ -1391,7 +1402,7 @@
}
@Override
- public void onAdd(WorkspaceItemInfo item, int rank) {
+ public void onAdd(ItemInfo item, int rank) {
FolderGridOrganizer verifier = new FolderGridOrganizer(
mActivityContext.getDeviceProfile()).setFolderInfo(mInfo);
verifier.updateRankAndPos(item, rank);
@@ -1406,7 +1417,7 @@
}
@Override
- public void onRemove(List<WorkspaceItemInfo> items) {
+ public void onRemove(List<ItemInfo> items) {
mItemsInvalidated = true;
items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
if (mState == STATE_ANIMATING) {
@@ -1423,7 +1434,7 @@
}
}
- private View getViewForInfo(final WorkspaceItemInfo item) {
+ private View getViewForInfo(final ItemInfo item) {
return mContent.iterateOverItems((info, view) -> info == item);
}
@@ -1432,6 +1443,11 @@
updateTextViewFocus();
}
+ @Override
+ public void onTitleChanged(CharSequence title) {
+ mFolderName.setText(title);
+ }
+
/**
* Utility methods to iterate over items of the view
*/
@@ -1451,7 +1467,7 @@
return mItemsInReadingOrder;
}
- public List<BubbleTextView> getItemsOnPage(int page) {
+ public List<View> getItemsOnPage(int page) {
ArrayList<View> allItems = getIconsInReadingOrder();
int lastPage = mContent.getPageCount() - 1;
int totalItemsInFolder = allItems.size();
@@ -1463,9 +1479,9 @@
int startIndex = page * itemsPerPage;
int endIndex = Math.min(startIndex + numItemsOnCurrentPage, allItems.size());
- List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
+ List<View> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
for (int i = startIndex; i < endIndex; ++i) {
- itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
+ itemsOnCurrentPage.add(allItems.get(i));
}
return itemsOnCurrentPage;
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index a91373b..7a2ec97 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -43,6 +43,7 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -127,7 +128,7 @@
(BaseDragLayer.LayoutParams) mFolder.getLayoutParams();
mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams();
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
- final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
+ final List<View> itemsInPreview = getPreviewIconsOnPage(0);
// Match position of the FolderIcon
final Rect folderIconPos = new Rect();
@@ -139,8 +140,8 @@
// Match size/scale of icons in the preview
float previewScale = rule.scaleForItem(itemsInPreview.size());
float previewSize = rule.getIconSize() * previewScale;
- float initialScale = previewSize / itemsInPreview.get(0).getIconSize()
- * scaleRelativeToDragLayer;
+ float baseIconSize = getBubbleTextView(itemsInPreview.get(0)).getIconSize();
+ float initialScale = previewSize / baseIconSize * scaleRelativeToDragLayer;
final float finalScale = 1f;
float scale = mIsOpening ? initialScale : finalScale;
mFolder.setPivotX(0);
@@ -198,11 +199,12 @@
// Initialize the Folder items' text.
PropertyResetListener colorResetListener =
new PropertyResetListener<>(TEXT_ALPHA_PROPERTY, 1f);
- for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
+ for (View icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
+ BubbleTextView titleText = getBubbleTextView(icon);
if (mIsOpening) {
- icon.setTextVisibility(false);
+ titleText.setTextVisibility(false);
}
- ObjectAnimator anim = icon.createTextAlphaAnimator(mIsOpening);
+ ObjectAnimator anim = titleText.createTextAlphaAnimator(mIsOpening);
anim.addListener(colorResetListener);
play(a, anim);
}
@@ -339,7 +341,7 @@
/**
* Returns the list of "preview items" on {@param page}.
*/
- private List<BubbleTextView> getPreviewIconsOnPage(int page) {
+ private List<View> getPreviewIconsOnPage(int page) {
return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
.previewItemsForPage(page, mFolder.getIconsInReadingOrder());
}
@@ -351,7 +353,7 @@
int previewItemOffsetX, int previewItemOffsetY) {
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
- final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(
+ final List<View> itemsInPreview = getPreviewIconsOnPage(
isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage());
final int numItemsInPreview = itemsInPreview.size();
final int numItemsInFirstPagePreview = isOnFirstPage
@@ -361,48 +363,49 @@
ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
for (int i = 0; i < numItemsInPreview; ++i) {
- final BubbleTextView btv = itemsInPreview.get(i);
- CellLayoutLayoutParams btvLp = (CellLayoutLayoutParams) btv.getLayoutParams();
+ final View v = itemsInPreview.get(i);
+ CellLayoutLayoutParams vLp = (CellLayoutLayoutParams) v.getLayoutParams();
// Calculate the final values in the LayoutParams.
- btvLp.isLockedToGrid = true;
- cwc.setupLp(btv);
+ vLp.isLockedToGrid = true;
+ cwc.setupLp(v);
// Match scale of icons in the preview of the items on the first page.
float previewScale = rule.scaleForItem(numItemsInFirstPagePreview);
float previewSize = rule.getIconSize() * previewScale;
- float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
+ float baseIconSize = getBubbleTextView(v).getIconSize();
+ float iconScale = previewSize / baseIconSize;
final float initialScale = iconScale / folderScale;
final float finalScale = 1f;
float scale = mIsOpening ? initialScale : finalScale;
- btv.setScaleX(scale);
- btv.setScaleY(scale);
+ v.setScaleX(scale);
+ v.setScaleY(scale);
// Match positions of the icons in the folder with their positions in the preview
rule.computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, mTmpParams);
// The PreviewLayoutRule assumes that the icon size takes up the entire width so we
// offset by the actual size.
- int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2;
+ int iconOffsetX = (int) ((vLp.width - baseIconSize) * iconScale) / 2;
final int previewPosX =
(int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale);
- final float paddingTop = btv.getPaddingTop() * iconScale;
+ final float paddingTop = v.getPaddingTop() * iconScale;
final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY - paddingTop)
/ folderScale);
- final float xDistance = previewPosX - btvLp.x;
- final float yDistance = previewPosY - btvLp.y;
+ final float xDistance = previewPosX - vLp.x;
+ final float yDistance = previewPosY - vLp.y;
- Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f);
+ Animator translationX = getAnimator(v, View.TRANSLATION_X, xDistance, 0f);
translationX.setInterpolator(previewItemInterpolator);
play(animatorSet, translationX);
- Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f);
+ Animator translationY = getAnimator(v, View.TRANSLATION_Y, yDistance, 0f);
translationY.setInterpolator(previewItemInterpolator);
play(animatorSet, translationY);
- Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale);
+ Animator scaleAnimator = getAnimator(v, SCALE_PROPERTY, initialScale, finalScale);
scaleAnimator.setInterpolator(previewItemInterpolator);
play(animatorSet, scaleAnimator);
@@ -426,20 +429,20 @@
super.onAnimationStart(animation);
// Necessary to initialize values here because of the start delay.
if (mIsOpening) {
- btv.setTranslationX(xDistance);
- btv.setTranslationY(yDistance);
- btv.setScaleX(initialScale);
- btv.setScaleY(initialScale);
+ v.setTranslationX(xDistance);
+ v.setTranslationY(yDistance);
+ v.setScaleX(initialScale);
+ v.setScaleY(initialScale);
}
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- btv.setTranslationX(0.0f);
- btv.setTranslationY(0.0f);
- btv.setScaleX(1f);
- btv.setScaleY(1f);
+ v.setTranslationX(0.0f);
+ v.setTranslationY(0.0f);
+ v.setScaleX(1f);
+ v.setScaleY(1f);
}
});
}
@@ -482,4 +485,15 @@
? ObjectAnimator.ofArgb(drawable, property, v1, v2)
: ObjectAnimator.ofArgb(drawable, property, v2, v1);
}
+
+ /**
+ * Gets the {@link com.android.launcher3.BubbleTextView} from an icon. In some cases the
+ * BubbleTextView is the whole icon itself, while in others it is contained within the view and
+ * only serves to store the title text.
+ */
+ private BubbleTextView getBubbleTextView(View v) {
+ return v instanceof AppPairIcon
+ ? ((AppPairIcon) v).getTitleTextView()
+ : (BubbleTextView) v;
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 62ce311..4d88b68 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -71,6 +71,7 @@
import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo.FolderListener;
import com.android.launcher3.model.data.FolderInfo.LabelState;
@@ -118,7 +119,7 @@
ClippedFolderIconLayoutRule mPreviewLayoutRule;
private PreviewItemManager mPreviewItemManager;
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
- private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>();
+ private List<ItemInfo> mCurrentPreviewItems = new ArrayList<>();
boolean mAnimating = false;
@@ -215,7 +216,7 @@
// Keep the notification dot up to date with the sum of all the content's dots.
FolderDotInfo folderDotInfo = new FolderDotInfo();
- for (WorkspaceItemInfo si : folderInfo.getContents()) {
+ for (ItemInfo si : folderInfo.getContents()) {
folderDotInfo.addDotInfo(activity.getDotInfoForItem(si));
}
icon.setDotInfo(folderDotInfo);
@@ -261,20 +262,18 @@
private boolean willAcceptItem(ItemInfo item) {
final int itemType = item.itemType;
- return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
- item != mInfo && !mFolder.isOpen());
+ return (Folder.willAcceptItemType(itemType) && item != mInfo && !mFolder.isOpen());
}
public boolean acceptDrop(ItemInfo dragInfo) {
return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
}
- public void addItem(WorkspaceItemInfo item) {
+ public void addItem(ItemInfo item) {
mInfo.add(item, true);
}
- public void removeItem(WorkspaceItemInfo item, boolean animate) {
+ public void removeItem(ItemInfo item, boolean animate) {
mInfo.remove(item, animate);
}
@@ -287,8 +286,8 @@
mOpenAlarm.setOnAlarmListener(mOnOpenListener);
if (SPRING_LOADING_ENABLED &&
((dragInfo instanceof WorkspaceItemFactory)
- || (dragInfo instanceof WorkspaceItemInfo)
- || (dragInfo instanceof PendingAddShortcutInfo))) {
+ || (dragInfo instanceof PendingAddShortcutInfo)
+ || Folder.willAccept(dragInfo))) {
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
}
}
@@ -303,8 +302,8 @@
return mPreviewItemManager.prepareCreateAnimation(destView);
}
- public void performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView,
- final WorkspaceItemInfo srcInfo, final DragObject d, Rect dstRect,
+ public void performCreateAnimation(final ItemInfo destInfo, final View destView,
+ final ItemInfo srcInfo, final DragObject d, Rect dstRect,
float scaleRelativeToDragLayer) {
final DragView srcView = d.dragView;
prepareCreateAnimation(destView);
@@ -330,7 +329,7 @@
mOpenAlarm.cancelAlarm();
}
- private void onDrop(final WorkspaceItemInfo item, DragObject d, Rect finalRect,
+ private void onDrop(final ItemInfo item, DragObject d, Rect finalRect,
float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) {
item.cellX = -1;
item.cellY = -1;
@@ -361,7 +360,7 @@
int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1);
boolean itemAdded = false;
if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
- List<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
+ List<ItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
mInfo.add(item, index, false);
mCurrentPreviewItems.clear();
mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0));
@@ -422,7 +421,7 @@
FolderNameInfos nameInfos = new FolderNameInfos();
Executors.MODEL_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
- getContext(), mInfo.getContents(), nameInfos);
+ getContext(), mInfo.getAppContents(), nameInfos);
postDelayed(() -> {
setLabelSuggestion(nameInfos, d.logInstanceId);
invalidate();
@@ -475,15 +474,21 @@
public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
- WorkspaceItemInfo item;
+ ItemInfo item;
if (d.dragInfo instanceof WorkspaceItemFactory) {
// Came from all apps -- make a copy
item = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(getContext());
} else if (d.dragSource instanceof BaseItemDragListener){
// Came from a different window -- make a copy
- item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo);
+ if (d.dragInfo instanceof AppPairInfo) {
+ // dragged item is app pair
+ item = new AppPairInfo((AppPairInfo) d.dragInfo);
+ } else {
+ // dragged item is WorkspaceItemInfo
+ item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo);
+ }
} else {
- item = (WorkspaceItemInfo) d.dragInfo;
+ item = d.dragInfo;
}
mFolder.notifyDrop();
onDrop(item, d, null, 1.0f,
@@ -665,7 +670,7 @@
/**
* Returns the list of items which should be visible in the preview
*/
- public List<WorkspaceItemInfo> getPreviewItemsOnPage(int page) {
+ public List<ItemInfo> getPreviewItemsOnPage(int page) {
return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.getContents());
}
@@ -690,12 +695,12 @@
/**
* Updates the preview items which match the provided condition
*/
- public void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
+ public void updatePreviewItems(Predicate<ItemInfo> itemCheck) {
mPreviewItemManager.updatePreviewItems(itemCheck);
}
@Override
- public void onAdd(WorkspaceItemInfo item, int rank) {
+ public void onAdd(ItemInfo item, int rank) {
updatePreviewItems(false);
boolean wasDotted = mDotInfo.hasDot();
mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
@@ -707,7 +712,7 @@
}
@Override
- public void onRemove(List<WorkspaceItemInfo> items) {
+ public void onRemove(List<ItemInfo> items) {
updatePreviewItems(false);
boolean wasDotted = mDotInfo.hasDot();
items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
@@ -718,6 +723,7 @@
requestLayout();
}
+ @Override
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(getAccessiblityTitle(title));
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index f2bed92..8eaa0dc 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -41,8 +41,10 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
@@ -148,7 +150,7 @@
/**
* Binds items to the layout.
*/
- public void bindItems(List<WorkspaceItemInfo> items) {
+ public void bindItems(List<ItemInfo> items) {
if (mViewsBound) {
unbindItems();
}
@@ -164,8 +166,11 @@
CellLayout page = (CellLayout) getChildAt(i);
ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets();
for (int j = container.getChildCount() - 1; j >= 0; j--) {
- container.getChildAt(j).setVisibility(View.VISIBLE);
- mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j));
+ View iconView = container.getChildAt(j);
+ iconView.setVisibility(View.VISIBLE);
+ if (iconView instanceof BubbleTextView) {
+ mViewCache.recycleView(R.layout.folder_application, iconView);
+ }
}
page.removeAllViews();
mViewCache.recycleView(R.layout.folder_page, page);
@@ -185,7 +190,7 @@
* Creates and adds an icon corresponding to the provided rank
* @return the created icon
*/
- public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) {
+ public View createAndAddViewForRank(ItemInfo item, int rank) {
View icon = createNewView(item);
if (!mViewsBound) {
return icon;
@@ -200,7 +205,7 @@
* Adds the {@param view} to the layout based on {@param rank} and updated the position
* related attributes. It assumes that {@param item} is already attached to the view.
*/
- public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
+ public void addViewForRank(View view, ItemInfo item, int rank) {
int pageNo = rank / mOrganizer.getMaxItemsPerPage();
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
@@ -209,26 +214,36 @@
}
@SuppressLint("InflateParams")
- public View createNewView(WorkspaceItemInfo item) {
+ public View createNewView(ItemInfo item) {
if (item == null) {
return null;
}
- final BubbleTextView textView = mViewCache.getView(
- R.layout.folder_application, getContext(), null);
- textView.applyFromWorkspaceItem(item);
- textView.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener());
- textView.setOnLongClickListener(mFolder);
- textView.setOnFocusChangeListener(mFocusIndicatorHelper);
- CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams();
+
+ final View icon;
+ if (item instanceof AppPairInfo api) {
+ // TODO (b/332607759): Make view cache work with app pair icons
+ icon = AppPairIcon.inflateIcon(R.layout.folder_app_pair, ActivityContext.lookupContext(
+ getContext()), null , api, BubbleTextView.DISPLAY_FOLDER);
+ } else {
+ icon = mViewCache.getView(R.layout.folder_application, getContext(), null);
+ ((BubbleTextView) icon).applyFromWorkspaceItem((WorkspaceItemInfo) item);
+ }
+
+ icon.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener());
+ icon.setOnLongClickListener(mFolder);
+ icon.setOnFocusChangeListener(mFocusIndicatorHelper);
+
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) icon.getLayoutParams();
if (lp == null) {
- textView.setLayoutParams(new CellLayoutLayoutParams(
+ icon.setLayoutParams(new CellLayoutLayoutParams(
item.cellX, item.cellY, item.spanX, item.spanY));
} else {
lp.setCellX(item.cellX);
lp.setCellY(item.cellY);
lp.cellHSpan = lp.cellVSpan = 1;
}
- return textView;
+
+ return icon;
}
@Nullable
@@ -497,13 +512,20 @@
if (page != null) {
ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
- BubbleTextView icon = ((BubbleTextView) parent.getChildAt(i));
- icon.verifyHighRes();
+ View iconView = parent.getChildAt(i);
+ Drawable d = null;
+ if (iconView instanceof BubbleTextView btv) {
+ btv.verifyHighRes();
+ d = btv.getIcon();
+ } else if (iconView instanceof AppPairIcon api) {
+ api.verifyHighRes();
+ d = api.getIconDrawableArea().getDrawable();
+ }
+
// Set the callback back to the actual icon, in case
// it was captured by the FolderIcon
- Drawable d = icon.getIcon();
if (d != null) {
- d.setCallback(icon);
+ d.setCallback(iconView);
}
}
}
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index 33bcf21..07215c4 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -33,7 +33,7 @@
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -86,7 +86,7 @@
FolderInfo info = folder.mInfo;
if (itemCount <= 1) {
View newIcon = null;
- WorkspaceItemInfo finalItem = null;
+ ItemInfo finalItem = null;
if (itemCount == 1) {
// Move the item from the folder to the workspace, in the position of the
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index 58efdc1..0faa1c9 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -17,7 +17,7 @@
import android.graphics.drawable.Drawable;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.model.data.ItemInfo;
/**
* Manages the parameters used to draw a Folder preview item.
@@ -30,7 +30,7 @@
public FolderPreviewItemAnim anim;
public boolean hidden;
public Drawable drawable;
- public WorkspaceItemInfo item;
+ public ItemInfo item;
PreviewItemDrawingParams(float transX, float transY, float scale) {
this.transX = transX;
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 9001a0c..6311638 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,6 +16,7 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
@@ -41,7 +42,12 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Utilities;
+import com.android.launcher3.apppairs.AppPairIcon;
+import com.android.launcher3.apppairs.AppPairIconDrawingParams;
+import com.android.launcher3.apppairs.AppPairIconGraphic;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.AppPairInfo;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Themes;
@@ -125,7 +131,9 @@
}
Drawable prepareCreateAnimation(final View destView) {
- Drawable animateDrawable = ((BubbleTextView) destView).getIcon();
+ Drawable animateDrawable = destView instanceof AppPairIcon
+ ? ((AppPairIcon) destView).getIconDrawableArea().getDrawable()
+ : ((BubbleTextView) destView).getIcon();
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
destView.getMeasuredWidth());
mReferenceDrawable = animateDrawable;
@@ -258,7 +266,7 @@
}
void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
- List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page);
+ List<ItemInfo> items = mIcon.getPreviewItemsOnPage(page);
// We adjust the size of the list to match the number of items in the preview.
while (items.size() < params.size()) {
@@ -328,16 +336,18 @@
mNumOfPrevItems = numOfPrevItemsAux;
}
- void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
+ void updatePreviewItems(Predicate<ItemInfo> itemCheck) {
boolean modified = false;
for (PreviewItemDrawingParams param : mFirstPageParams) {
- if (itemCheck.test(param.item)) {
+ if (itemCheck.test(param.item)
+ || (param.item instanceof AppPairInfo api && api.anyMatch(itemCheck))) {
setDrawable(param, param.item);
modified = true;
}
}
for (PreviewItemDrawingParams param : mCurrentPageParams) {
- if (itemCheck.test(param.item)) {
+ if (itemCheck.test(param.item)
+ || (param.item instanceof AppPairInfo api && api.anyMatch(itemCheck))) {
setDrawable(param, param.item);
modified = true;
}
@@ -370,15 +380,14 @@
* @param newItems The list of items in the new preview.
* @param dropped The item that was dropped onto the FolderIcon.
*/
- public void onDrop(List<WorkspaceItemInfo> oldItems, List<WorkspaceItemInfo> newItems,
- WorkspaceItemInfo dropped) {
+ public void onDrop(List<ItemInfo> oldItems, List<ItemInfo> newItems, ItemInfo dropped) {
int numItems = newItems.size();
final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
buildParamsForPage(0, params, false);
// New preview items for items that are moving in (except for the dropped item).
- List<WorkspaceItemInfo> moveIn = new ArrayList<>();
- for (WorkspaceItemInfo newItem : newItems) {
+ List<ItemInfo> moveIn = new ArrayList<>();
+ for (ItemInfo newItem : newItems) {
if (!oldItems.contains(newItem) && !newItem.equals(dropped)) {
moveIn.add(newItem);
}
@@ -401,10 +410,10 @@
}
// Old preview items that need to be moved out.
- List<WorkspaceItemInfo> moveOut = new ArrayList<>(oldItems);
+ List<ItemInfo> moveOut = new ArrayList<>(oldItems);
moveOut.removeAll(newItems);
for (int i = 0; i < moveOut.size(); ++i) {
- WorkspaceItemInfo item = moveOut.get(i);
+ ItemInfo item = moveOut.get(i);
int oldIndex = oldItems.indexOf(item);
PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null);
updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems);
@@ -418,7 +427,7 @@
}
}
- private void updateTransitionParam(final PreviewItemDrawingParams p, WorkspaceItemInfo item,
+ private void updateTransitionParam(final PreviewItemDrawingParams p, ItemInfo item,
int prevIndex, int newIndex, int numItems) {
setDrawable(p, item);
@@ -431,16 +440,24 @@
}
@VisibleForTesting
- public void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
- if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
- PreloadIconDrawable drawable = newPendingIcon(mContext, item);
- p.drawable = drawable;
- } else {
- p.drawable = item.newIcon(mContext,
- Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0);
+ public void setDrawable(PreviewItemDrawingParams p, ItemInfo item) {
+ if (item instanceof WorkspaceItemInfo wii) {
+ if (wii.hasPromiseIconUi() || (wii.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+ PreloadIconDrawable drawable = newPendingIcon(mContext, wii);
+ p.drawable = drawable;
+ } else {
+ p.drawable = wii.newIcon(mContext,
+ Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0);
+ }
+ p.drawable.setBounds(0, 0, mIconSize, mIconSize);
+ } else if (item instanceof AppPairInfo api) {
+ AppPairIconDrawingParams appPairParams =
+ new AppPairIconDrawingParams(mContext, DISPLAY_FOLDER);
+ p.drawable = AppPairIconGraphic.composeDrawable(api, appPairParams);
+ p.drawable.setBounds(0, 0, mIconSize, mIconSize);
}
- p.drawable.setBounds(0, 0, mIconSize, mIconSize);
+
p.item = item;
// Set the callback to FolderIcon as it is responsible to drawing the icon. The
// callback will be released when the folder is opened.
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 44e45da..d5de4ce 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -44,6 +44,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.CollectionInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -259,10 +260,15 @@
itemsIdMap.put(item.id, item);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
- collections.put(item.id, (CollectionInfo) item);
+ collections.put(item.id, (FolderInfo) item);
workspaceItems.add(item);
break;
+ case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
+ collections.put(item.id, (AppPairInfo) item);
+ // Fall through here. App pairs are both containers (like folders) and containable
+ // items (can be placed in folders). So we need to add app pairs to the folders
+ // array (above) but also verify the existence of their container, like regular
+ // apps (below).
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
@@ -277,7 +283,7 @@
Log.e(TAG, msg);
}
} else {
- findOrMakeFolder(item.container).add((WorkspaceItemInfo) item);
+ findOrMakeFolder(item.container).add(item);
}
}
break;
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 1deb665..cc20cd1 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -115,7 +115,7 @@
for (ItemInfo info : firstScreenItems) {
if (info instanceof CollectionInfo ci) {
String collectionItemInfoPackage;
- for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getContents())) {
+ for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getAppContents())) {
collectionItemInfoPackage = getPackageName(collectionItemInfo);
if (collectionItemInfoPackage != null
&& packages.contains(collectionItemInfoPackage)) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 299c952..f24a7c1 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -49,6 +49,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -674,6 +675,11 @@
private String mProvider;
private Map<String, Set<Integer>> mFolderItems = new HashMap<>();
+ /**
+ * Id of the specific widget.
+ */
+ public int appWidgetId = NO_ID;
+
/** Comparator according to the reading order */
@Override
public int compareTo(DbEntry another) {
@@ -707,6 +713,18 @@
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
+ @Override
+ public void writeToValues(@NonNull ContentWriter writer) {
+ super.writeToValues(writer);
+ writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+ }
+
+ @Override
+ public void readFromValues(@NonNull ContentValues values) {
+ super.readFromValues(values);
+ appWidgetId = values.getAsInteger(LauncherSettings.Favorites.APPWIDGET_ID);
+ }
+
/** This id is not used in the DB is only used while doing the migration and it identifies
* an entry on each workspace. For example two calculator icons would have the same
* migration id even thought they have different database ids.
@@ -717,7 +735,10 @@
case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
return getFolderMigrationId();
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- return mProvider;
+ // mProvider is the app the widget belongs to and appWidgetId it's the unique
+ // is of the widget, we need both because if you remove a widget and then add it
+ // again, then it can change and the WidgetProvider would not know the widget.
+ return mProvider + appWidgetId;
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
final String intentStr = cleanIntentString(mIntent);
try {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 30cccd5..e0ced83 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -81,7 +81,6 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
@@ -494,9 +493,7 @@
}
appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR);
- // Fetch hi-res icons if needed.
- appPair.getContents().stream().filter(ItemInfoWithIcon::usingLowResIcon)
- .forEach(member -> mIconCache.getTitleAndIcon(member, false));
+ appPair.fetchHiResIconsIfNeeded(mIconCache);
}
}
@@ -566,12 +563,16 @@
// Ranks are the source of truth for folder items, so cellX and cellY can be
// ignored for now. Database will be updated once user manually modifies folder.
for (int rank = 0; rank < size; ++rank) {
- WorkspaceItemInfo info = folder.getContents().get(rank);
+ ItemInfo info = folder.getContents().get(rank);
info.rank = rank;
- if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION
+ if (info instanceof WorkspaceItemInfo wii
+ && wii.usingLowResIcon()
+ && wii.itemType == Favorites.ITEM_TYPE_APPLICATION
&& verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) {
- mIconCache.getTitleAndIcon(info, false);
+ mIconCache.getTitleAndIcon(wii, false);
+ } else if (info instanceof AppPairInfo api) {
+ api.fetchHiResIconsIfNeeded(mIconCache);
}
}
}
@@ -788,7 +789,7 @@
FolderNameInfos suggestionInfos = new FolderNameInfos();
CollectionInfo info = mBgDataModel.collections.valueAt(i);
if (info instanceof FolderInfo fi && fi.suggestedFolderNames == null) {
- provider.getSuggestedFolderName(mApp.getContext(), fi.getContents(),
+ provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
suggestionInfos);
fi.suggestedFolderNames = suggestionInfos;
}
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index aa29290..d27be72 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -375,7 +375,7 @@
val folderInfo: FolderInfo = collection
val newAppPair = AppPairInfo()
// Move the placeholder's contents over to the new app pair.
- folderInfo.contents.forEach(newAppPair::add)
+ folderInfo.getContents().forEach(newAppPair::add)
collection = newAppPair
// Remove the placeholder and add the app pair into the data model.
bgDataModel.collections.remove(c.id)
diff --git a/src/com/android/launcher3/model/data/AppPairInfo.kt b/src/com/android/launcher3/model/data/AppPairInfo.kt
index 4081316..63c77bb 100644
--- a/src/com/android/launcher3/model/data/AppPairInfo.kt
+++ b/src/com/android/launcher3/model/data/AppPairInfo.kt
@@ -18,11 +18,14 @@
import android.content.Context
import com.android.launcher3.LauncherSettings
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.views.ActivityContext
/** A type of app collection that launches multiple apps into split screen. */
class AppPairInfo() : CollectionInfo() {
+ private var contents: ArrayList<WorkspaceItemInfo> = ArrayList()
+
init {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR
}
@@ -33,11 +36,28 @@
add(app2)
}
- /** Adds an element to the contents array. */
- override fun add(item: WorkspaceItemInfo) {
+ /** Creates a new AppPairInfo that is a copy of the provided one. */
+ constructor(appPairInfo: AppPairInfo) : this() {
+ contents = appPairInfo.contents.clone() as ArrayList<WorkspaceItemInfo>
+ copyFrom(appPairInfo)
+ }
+
+ /** Adds an element to the contents ArrayList. */
+ override fun add(item: ItemInfo) {
+ if (item !is WorkspaceItemInfo) {
+ throw RuntimeException("tried to add an illegal type into an app pair")
+ }
+
contents.add(item)
}
+ /** Returns the app pair's member apps as an ArrayList of [ItemInfo]. */
+ override fun getContents(): ArrayList<ItemInfo> =
+ ArrayList(contents.stream().map { it as ItemInfo }.toList())
+
+ /** Returns the app pair's member apps as an ArrayList of [WorkspaceItemInfo]. */
+ override fun getAppContents(): ArrayList<WorkspaceItemInfo> = contents
+
/** Returns the first app in the pair. */
fun getFirstApp() = contents[0]
@@ -50,7 +70,16 @@
/** Checks if the app pair is launchable at the current screen size. */
fun isLaunchable(context: Context) =
(ActivityContext.lookupContext(context) as ActivityContext).getDeviceProfile().isTablet ||
- noneMatch { it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE) }
+ getAppContents().stream().noneMatch {
+ it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE)
+ }
+
+ /** Fetches high-res icons for member apps if needed. */
+ fun fetchHiResIconsIfNeeded(iconCache: IconCache) {
+ getAppContents().stream().filter(ItemInfoWithIcon::usingLowResIcon).forEach { member ->
+ iconCache.getTitleAndIcon(member, false)
+ }
+ }
/** Generates an ItemInfo for logging. */
override fun buildProto(cInfo: CollectionInfo?): LauncherAtom.ItemInfo {
diff --git a/src/com/android/launcher3/model/data/CollectionInfo.kt b/src/com/android/launcher3/model/data/CollectionInfo.kt
index 2b865a5..4f5e12f 100644
--- a/src/com/android/launcher3/model/data/CollectionInfo.kt
+++ b/src/com/android/launcher3/model/data/CollectionInfo.kt
@@ -22,19 +22,21 @@
import java.util.function.Predicate
abstract class CollectionInfo : ItemInfo() {
- var contents: ArrayList<WorkspaceItemInfo> = ArrayList()
+ /** Adds an ItemInfo to the collection. Throws if given an illegal type. */
+ abstract fun add(item: ItemInfo)
- abstract fun add(item: WorkspaceItemInfo)
+ /** Returns the collection's contents as an ArrayList of [ItemInfo]. */
+ abstract fun getContents(): ArrayList<ItemInfo>
+
+ /**
+ * Returns the collection's contents as an ArrayList of [WorkspaceItemInfo]. Does not include
+ * other collection [ItemInfo]s that are inside this collection; rather, it should collect
+ * *their* contents and adds them to the ArrayList.
+ */
+ abstract fun getAppContents(): ArrayList<WorkspaceItemInfo>
/** Convenience function. Checks contents to see if any match a given predicate. */
- fun anyMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
- return contents.stream().anyMatch(matcher)
- }
-
- /** Convenience function. Returns true if none of the contents match a given predicate. */
- fun noneMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
- return contents.stream().noneMatch(matcher)
- }
+ fun anyMatch(matcher: Predicate<ItemInfo>) = getContents().stream().anyMatch(matcher)
override fun onAddToDatabase(writer: ContentWriter) {
super.onAddToDatabase(writer)
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 1bbb2fe..18d2b85 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -29,6 +29,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.Attribute;
@@ -98,14 +99,20 @@
public FolderNameInfos suggestedFolderNames;
+ /**
+ * The apps and shortcuts
+ */
+ private final ArrayList<ItemInfo> contents = new ArrayList<>();
+
private ArrayList<FolderListener> mListeners = new ArrayList<>();
public FolderInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
}
- /** Adds a app or shortcut to the contents array without animation. */
- public void add(@NonNull WorkspaceItemInfo item) {
+ /** Adds a app or shortcut to the contents ArrayList without animation. */
+ @Override
+ public void add(@NonNull ItemInfo item) {
add(item, false /* animate */);
}
@@ -114,14 +121,18 @@
*
* @param item
*/
- public void add(WorkspaceItemInfo item, boolean animate) {
+ public void add(ItemInfo item, boolean animate) {
add(item, getContents().size(), animate);
}
/**
* Add an app or shortcut for a specified rank.
*/
- public void add(WorkspaceItemInfo item, int rank, boolean animate) {
+ public void add(ItemInfo item, int rank, boolean animate) {
+ if (!Folder.willAccept(item)) {
+ throw new RuntimeException("tried to add an illegal type into a folder");
+ }
+
rank = Utilities.boundToRange(rank, 0, getContents().size());
getContents().add(rank, item);
for (int i = 0; i < mListeners.size(); i++) {
@@ -135,21 +146,49 @@
*
* @param item
*/
- public void remove(WorkspaceItemInfo item, boolean animate) {
+ public void remove(ItemInfo item, boolean animate) {
removeAll(Collections.singletonList(item), animate);
}
/**
* Remove all matching app or shortcut. Does not change the DB.
*/
- public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
- getContents().removeAll(items);
+ public void removeAll(List<ItemInfo> items, boolean animate) {
+ contents.removeAll(items);
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onRemove(items);
}
itemsChanged(animate);
}
+ /**
+ * Returns the folder's contents as an ArrayList of {@link ItemInfo}. Includes
+ * {@link WorkspaceItemInfo} and {@link AppPairInfo}s.
+ */
+ @NonNull
+ @Override
+ public ArrayList<ItemInfo> getContents() {
+ return contents;
+ }
+
+ /**
+ * Returns the folder's contents as an ArrayList of {@link WorkspaceItemInfo}. Note: Does not
+ * return any {@link AppPairInfo}s contained in the folder, instead collects *their* contents
+ * and adds them to the ArrayList.
+ */
+ @Override
+ public ArrayList<WorkspaceItemInfo> getAppContents() {
+ ArrayList<WorkspaceItemInfo> workspaceItemInfos = new ArrayList<>();
+ for (ItemInfo item : contents) {
+ if (item instanceof WorkspaceItemInfo wii) {
+ workspaceItemInfos.add(wii);
+ } else if (item instanceof AppPairInfo api) {
+ workspaceItemInfos.addAll(api.getAppContents());
+ }
+ }
+ return workspaceItemInfos;
+ }
+
@Override
public void onAddToDatabase(@NonNull ContentWriter writer) {
super.onAddToDatabase(writer);
@@ -171,9 +210,11 @@
}
public interface FolderListener {
- void onAdd(WorkspaceItemInfo item, int rank);
- void onRemove(List<WorkspaceItemInfo> item);
+ void onAdd(ItemInfo item, int rank);
+ void onRemove(List<ItemInfo> item);
void onItemsChanged(boolean animate);
+ void onTitleChanged(CharSequence title);
+
}
public boolean hasOption(int optionFlag) {
@@ -246,6 +287,10 @@
if (modelWriter != null) {
modelWriter.updateItemInDatabase(this);
}
+
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onTitleChanged(title);
+ }
}
/**
@@ -263,10 +308,17 @@
public ItemInfo makeShallowCopy() {
FolderInfo folderInfo = new FolderInfo();
folderInfo.copyFrom(this);
- folderInfo.setContents(this.getContents());
return folderInfo;
}
+ @Override
+ public void copyFrom(@NonNull ItemInfo info) {
+ super.copyFrom(info);
+ if (info instanceof FolderInfo fi) {
+ contents.addAll(fi.getContents());
+ }
+ }
+
/**
* Returns index of the accepted suggestion.
*/
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 9fbc6bf..be3aa10 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -168,9 +168,9 @@
return (runtimeStatusFlags & FLAG_ARCHIVED) != 0;
}
- /** Returns true if the app is archived and has an active install session. */
- public boolean isActiveArchive() {
- return isArchived() && (runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0;
+ /** Returns true if the app is archived and doesn't have an active install session. */
+ public boolean isInactiveArchive() {
+ return isArchived() && (runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0;
}
/**
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 9f2b10f..b4e6365 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -126,6 +126,9 @@
if (Flags.enableNarrowGridRestore()) {
String oldPhoneFileName = idp.dbFile;
removeOldDBs(context, oldPhoneFileName);
+ // The idp before this contains data about the old phone, after this it becomes the idp
+ // of the current phone.
+ idp.reset(context);
trySettingPreviousGidAsCurrent(context, idp, oldPhoneFileName);
} else {
idp.reinitializeAfterRestore(context);
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 116f13a..89057a2 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -39,6 +39,7 @@
import com.android.launcher3.folder.Folder;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PrivateSpaceInstallAppButtonInfo;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.views.BubbleTextHolder;
@@ -150,7 +151,10 @@
if (launcher.getWorkspace().isSwitchingState()) return false;
StatsLogger logger = launcher.getStatsLogManager().logger();
- if (v.getTag() instanceof ItemInfo) {
+ if (v.getTag() instanceof ItemInfo itemInfo) {
+ if (itemInfo instanceof PrivateSpaceInstallAppButtonInfo) {
+ return false;
+ }
logger.withItemInfo((ItemInfo) v.getTag());
}
logger.log(LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED);
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 4c9371d..bac3345 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -82,7 +82,6 @@
};
protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
- private static final float VIEW_NO_SCALE = 1f;
private static final int DEFAULT_DURATION = 300;
protected final T mActivityContext;
@@ -129,9 +128,13 @@
protected @Nullable OnCloseListener mOnCloseBeginListener;
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
- protected final AnimatedFloat mSlideInViewScale =
- new AnimatedFloat(this::onScaleProgressChanged, VIEW_NO_SCALE);
- protected boolean mIsBackProgressing;
+ /**
+ * How far through a "user initiated dismissal" the UI is. e.g. Predictive back, swipe to home,
+ * 0 is regular state, 1 is fully dismissed.
+ */
+ protected final AnimatedFloat mSwipeToDismissProgress =
+ new AnimatedFloat(this::onUserSwipeToDismissProgressChanged, 0f);
+ protected boolean mIsDismissInProgress;
private @Nullable Drawable mContentBackground;
private @Nullable View mContentBackgroundParentView;
@@ -287,29 +290,30 @@
final float progress = backEvent.getProgress();
float deceleratedProgress =
Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress);
- mIsBackProgressing = progress > 0f;
- mSlideInViewScale.updateValue(PREDICTIVE_BACK_MIN_SCALE
- + (1 - PREDICTIVE_BACK_MIN_SCALE) * (1 - deceleratedProgress));
+ mSwipeToDismissProgress.updateValue(deceleratedProgress);
}
- protected void onScaleProgressChanged() {
- float scaleProgress = mSlideInViewScale.value;
- SCALE_PROPERTY.set(this, scaleProgress);
- setClipChildren(!mIsBackProgressing);
- setClipToPadding(!mIsBackProgressing);
- mContent.setClipChildren(!mIsBackProgressing);
- mContent.setClipToPadding(!mIsBackProgressing);
+ protected void onUserSwipeToDismissProgressChanged() {
+ float progress = mSwipeToDismissProgress.value;
+ mIsDismissInProgress = progress > 0f;
+
+ float scale = PREDICTIVE_BACK_MIN_SCALE + (1 - PREDICTIVE_BACK_MIN_SCALE) * (1f - progress);
+ SCALE_PROPERTY.set(this, scale);
+ setClipChildren(!mIsDismissInProgress);
+ setClipToPadding(!mIsDismissInProgress);
+ mContent.setClipChildren(!mIsDismissInProgress);
+ mContent.setClipToPadding(!mIsDismissInProgress);
invalidate();
}
@Override
public void onBackCancelled() {
super.onBackCancelled();
- animateSlideInViewToNoScale();
+ animateSwipeToDismissProgressToStart();
}
- protected void animateSlideInViewToNoScale() {
- mSlideInViewScale.animateToValue(1f)
+ protected void animateSwipeToDismissProgressToStart() {
+ mSwipeToDismissProgress.animateToValue(0f)
.setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
.start();
}
@@ -340,7 +344,7 @@
mContentBackgroundParentView.getTop() + (int) mContent.getTranslationY(),
mContentBackgroundParentView.getRight(),
mContentBackgroundParentView.getBottom()
- + (mIsBackProgressing ? getBottomOffsetPx() : 0));
+ + (mIsDismissInProgress ? getBottomOffsetPx() : 0));
mContentBackground.draw(canvas);
}
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index 45ff9de..53fbd8f 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -70,9 +70,9 @@
}
@Override
- protected void onScaleProgressChanged() {
- super.onScaleProgressChanged();
- setTranslationY(getMeasuredHeight() * (1 - mSlideInViewScale.value) / 2);
+ protected void onUserSwipeToDismissProgressChanged() {
+ super.onUserSwipeToDismissProgressChanged();
+ setTranslationY(getMeasuredHeight() * (mSwipeToDismissProgress.value / 2));
}
private void show() {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 3dff555..ab007fb 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.os.Process;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -39,7 +38,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -88,7 +86,6 @@
private Size mPreviewContainerSize = new Size(0, 0);
private FrameLayout mWidgetImageContainer;
private WidgetImageView mWidgetImage;
- private ImageView mWidgetBadge;
private TextView mWidgetName;
private TextView mWidgetDims;
private TextView mWidgetDescription;
@@ -142,7 +139,6 @@
mWidgetImageContainer = findViewById(R.id.widget_preview_container);
mWidgetImage = findViewById(R.id.widget_preview);
- mWidgetBadge = findViewById(R.id.widget_badge);
mWidgetName = findViewById(R.id.widget_name);
mWidgetDims = findViewById(R.id.widget_dims);
mWidgetDescription = findViewById(R.id.widget_description);
@@ -182,8 +178,6 @@
mWidgetImage.animate().cancel();
mWidgetImage.setDrawable(null);
mWidgetImage.setVisibility(View.VISIBLE);
- mWidgetBadge.setImageDrawable(null);
- mWidgetBadge.setVisibility(View.GONE);
mWidgetName.setText(null);
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
@@ -397,17 +391,6 @@
}
}
- /** Used to show the badge when the widget is in the recommended section
- */
- public void showBadge() {
- if (Process.myUserHandle().equals(mItem.user)) {
- mWidgetBadge.setVisibility(View.GONE);
- } else {
- mWidgetBadge.setVisibility(View.VISIBLE);
- mWidgetBadge.setImageResource(R.drawable.ic_work_app_badge);
- }
- }
-
/**
* Shows or hides the long description displayed below each widget.
*
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index f0a23be..f332054 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -24,8 +24,6 @@
import android.util.AttributeSet;
import android.view.View;
-import com.android.launcher3.R;
-
/**
* View that draws a bitmap horizontally centered. If the image width is greater than the view
* width, the image is scaled down appropriately.
@@ -33,8 +31,6 @@
public class WidgetImageView extends View {
private final RectF mDstRectF = new RectF();
- private final int mBadgeMargin;
-
private Drawable mDrawable;
public WidgetImageView(Context context) {
@@ -47,9 +43,6 @@
public WidgetImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
- mBadgeMargin = context.getResources()
- .getDimensionPixelSize(R.dimen.profile_badge_margin);
}
/** Set the drawable to use for this view. */
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index e6b9c9b..f1b80e4 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -280,6 +280,6 @@
@Override
public void addHintCloseAnim(
float distanceToMove, Interpolator interpolator, PendingAnimation target) {
- target.setInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom), interpolator);
+ target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index fcd2571..85375ee 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_DIALOG_SEEN;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
@@ -548,6 +547,14 @@
public void exitSearchMode() {
if (!mIsInSearchMode) return;
onSearchResults(new ArrayList<>());
+ WidgetsRecyclerView searchRecyclerView = mAdapters.get(
+ AdapterHolder.SEARCH).mWidgetsRecyclerView;
+ // Remove all views when exiting the search mode; this prevents animating from stale results
+ // to new ones the next time we enter search mode. By the time recycler view is hidden,
+ // layout may not have happened to clear up existing results. So, instead of waiting for it
+ // to happen, we clear the views here.
+ searchRecyclerView.swapAdapter(
+ searchRecyclerView.getAdapter(), /*removeAndRecycleExistingViews=*/ true);
setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
if (mHasWorkProfile) {
mViewPager.snapToPage(AdapterHolder.PRIMARY);
@@ -806,8 +813,7 @@
@Override
public void addHintCloseAnim(
float distanceToMove, Interpolator interpolator, PendingAnimation target) {
- target.setFloat(getRecyclerView(), VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
- target.setViewAlpha(getRecyclerView(), 0.5f, interpolator);
+ target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
}
@Override
@@ -903,7 +909,7 @@
public void onBackInvoked() {
if (mIsInSearchMode) {
mSearchBar.reset();
- animateSlideInViewToNoScale();
+ animateSwipeToDismissProgressToStart();
} else {
super.onBackInvoked();
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 7a2b4ef..563894d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -109,7 +109,6 @@
WidgetCell widgetCell = addItemCell(tableRow);
widgetCell.applyFromCellItem(widgetItem);
widgetCell.showAppIconInWidgetTitle(true);
- widgetCell.showBadge();
if (enableCategorizedWidgetSuggestions()) {
widgetCell.showDescription(false);
widgetCell.showDimensions(false);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index c60bca0..1bf813c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -74,7 +74,7 @@
private ScrollView mRightPaneScrollView;
private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
- private boolean mOldIsBackSwipeProgressing;
+ private boolean mOldIsSwipeToDismissInProgress;
private int mActivePage = -1;
private PackageUserKey mSelectedHeader;
@@ -154,14 +154,14 @@
}
@Override
- protected void onScaleProgressChanged() {
- super.onScaleProgressChanged();
- boolean isBackSwipeProgressing = mSlideInViewScale.value > 0;
- if (isBackSwipeProgressing == mOldIsBackSwipeProgressing) {
+ protected void onUserSwipeToDismissProgressChanged() {
+ super.onUserSwipeToDismissProgressChanged();
+ boolean isSwipeToDismissInProgress = mSwipeToDismissProgress.value > 0;
+ if (isSwipeToDismissInProgress == mOldIsSwipeToDismissInProgress) {
return;
}
- mOldIsBackSwipeProgressing = isBackSwipeProgressing;
- if (isBackSwipeProgressing) {
+ mOldIsSwipeToDismissInProgress = isSwipeToDismissInProgress;
+ if (isSwipeToDismissInProgress) {
modifyAttributesOnViewTree(mPrimaryWidgetListView, (ViewParent) mContent,
CLIP_CHILDREN_FALSE_MODIFIER);
modifyAttributesOnViewTree(mRightPaneScrollView, (ViewParent) mContent,
diff --git a/tests/src/com/android/launcher3/LauncherIntentTest.java b/tests/src/com/android/launcher3/LauncherIntentTest.java
index e8822c3..a3013c7 100644
--- a/tests/src/com/android/launcher3/LauncherIntentTest.java
+++ b/tests/src/com/android/launcher3/LauncherIntentTest.java
@@ -29,7 +29,6 @@
import com.android.launcher3.allapps.SearchRecyclerView;
import com.android.launcher3.ui.AbstractLauncherUiTest;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +39,6 @@
public final Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
@Test
- @Ignore("b/329152799")
public void testAllAppsIntent() {
// setup by moving to home
mLauncher.goHome();
@@ -66,8 +64,6 @@
// Highlights the search bar, then fills text to display the SearchView.
private void moveToSearchView() {
- mLauncher.goHome().switchToAllApps();
-
// All Apps view should be loaded
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 476bda5..e9d2f6e 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -26,7 +26,6 @@
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
@@ -39,6 +38,7 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -97,6 +97,8 @@
private LauncherApps mLauncherApps;
@Mock
private AllAppsRecyclerView mAllAppsRecyclerView;
+ @Mock
+ private Resources mResources;
@Before
public void setUp() {
@@ -106,6 +108,7 @@
when(mUserCache.getUserInfo(Process.myUserHandle())).thenReturn(MAIN_ICON_INFO);
when(mUserCache.getUserInfo(PRIVATE_HANDLE)).thenReturn(PRIVATE_ICON_INFO);
when(mAllApps.getContext()).thenReturn(mContext);
+ when(mAllApps.getContext().getResources()).thenReturn(mResources);
when(mAllApps.getAppsStore()).thenReturn(mAllAppsStore);
when(mAllApps.getActiveRecyclerView()).thenReturn(mAllAppsRecyclerView);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
@@ -145,6 +148,7 @@
public void quietModeFlagPresent_privateSpaceIsResetToDisabled() {
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+ doNothing().when(privateProfileManager).executeLock();
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
.thenReturn(false, true);
@@ -181,6 +185,7 @@
public void transitioningToLocked_resetCallsExecuteLock() throws Exception {
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+ doNothing().when(privateProfileManager).executeLock();
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
.thenReturn(true);
doNothing().when(privateProfileManager).expandPrivateSpace();
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
index 7ac276a..351b921 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
@@ -303,7 +303,7 @@
assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(position,
- privateProfileManager.scrollForViewToBeVisibleInContainer(
+ privateProfileManager.scrollForHeaderToBeVisibleInContainer(
new AllAppsRecyclerView(mContext),
mAlphabeticalAppsList.getAdapterItems(),
PS_HEADER_HEIGHT,
@@ -339,7 +339,7 @@
assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
mAlphabeticalAppsList.getAdapterItems().size());
assertEquals(position,
- privateProfileManager.scrollForViewToBeVisibleInContainer(
+ privateProfileManager.scrollForHeaderToBeVisibleInContainer(
new AllAppsRecyclerView(mContext),
mAlphabeticalAppsList.getAdapterItems(),
BIGGER_PS_HEADER_HEIGHT,
@@ -369,7 +369,7 @@
// The number of adapterItems should be the private space apps + one main app.
assertEquals(NUM_PRIVATE_SPACE_APPS + 1,
mAlphabeticalAppsList.getAdapterItems().size());
- assertEquals(SCROLL_NO_WHERE, privateProfileManager.scrollForViewToBeVisibleInContainer(
+ assertEquals(SCROLL_NO_WHERE, privateProfileManager.scrollForHeaderToBeVisibleInContainer(
new AllAppsRecyclerView(mContext),
mAlphabeticalAppsList.getAdapterItems(),
BIGGER_PS_HEADER_HEIGHT,
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
index e773a86..a006fd7 100644
--- a/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
+++ b/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
@@ -31,16 +31,17 @@
* account for cases where the user have the same item multiple times.
*/
fun generateItemsForTest(
- testCase: GridMigrationUnitTestCase,
- repeatAfter: Int
+ boards: List<CellLayoutBoard>,
+ repeatAfterRange: Point
): List<WorkspaceItem> {
val id = AtomicInteger(0)
val widgetId = AtomicInteger(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - 1)
- val boards = testCase.boards
// Repeat the same appWidgetProvider and intent to have repeating widgets and icons and test
// that case too
- val getIntent = { i: Int -> "Intent ${i % repeatAfter}" }
- val getProvider = { i: Int -> "com.test/test.Provider${i % repeatAfter}" }
+ val getIntent = { i: Int -> "Intent ${(i + repeatAfterRange.x) % repeatAfterRange.y}" }
+ val getProvider = { i: Int ->
+ "com.test/test.Provider${(i + repeatAfterRange.x) % repeatAfterRange.y }"
+ }
val hotseatEntries =
(0 until boards[0].width).map {
WorkspaceItem(
@@ -97,11 +98,12 @@
container = LauncherSettings.Favorites.CONTAINER_DESKTOP
)
}
- return widgetEntries + hotseatEntries // + iconEntries
+ return widgetEntries + hotseatEntries + iconEntries
}
data class GridMigrationUnitTestCase(
val boards: List<CellLayoutBoard>,
+ val destBoards: List<CellLayoutBoard>,
val srcSize: Point,
val targetSize: Point,
val seed: Long
@@ -135,11 +137,27 @@
return boards
}
- fun generateTestCase(): GridMigrationUnitTestCase {
- var seed = generator.nextLong()
+ fun generateTestCase(isDestEmpty: Boolean): GridMigrationUnitTestCase {
+ val seed = generator.nextLong()
val randomBoardGenerator = RandomBoardGenerator(Random(seed))
val width = randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
val height = randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+ val targetSize =
+ Point(
+ randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE),
+ randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+ )
+ val destBoards =
+ if (isDestEmpty) {
+ listOf()
+ } else {
+ generateBoards(
+ boardGenerator = randomBoardGenerator,
+ width = targetSize.x,
+ height = targetSize.y,
+ boardCount = randomBoardGenerator.getRandom(3, MAX_BOARD_COUNT)
+ )
+ }
return GridMigrationUnitTestCase(
boards =
generateBoards(
@@ -148,12 +166,9 @@
height = height,
boardCount = randomBoardGenerator.getRandom(3, MAX_BOARD_COUNT)
),
+ destBoards = destBoards,
srcSize = Point(width, height),
- targetSize =
- Point(
- randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE),
- randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
- ),
+ targetSize = targetSize,
seed = seed
)
}
diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index 3a1883c..da14425 100644
--- a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -29,7 +29,7 @@
import com.android.launcher3.icons.FastBitmapDrawable
import com.android.launcher3.icons.UserBadgeDrawable
import com.android.launcher3.model.data.FolderInfo
-import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.util.ActivityContextWrapper
import com.android.launcher3.util.FlagOp
import com.android.launcher3.util.LauncherLayoutBuilder
@@ -47,7 +47,7 @@
private lateinit var previewItemManager: PreviewItemManager
private lateinit var context: Context
- private lateinit var folderItems: ArrayList<WorkspaceItemInfo>
+ private lateinit var folderItems: ArrayList<ItemInfo>
private lateinit var modelHelper: LauncherModelHelper
private lateinit var folderIcon: FolderIcon
@@ -72,15 +72,17 @@
.build()
)
.loadModelSync()
- folderItems = modelHelper.bgDataModel.collections.valueAt(0).contents
+ folderItems = modelHelper.bgDataModel.collections.valueAt(0).getContents()
folderIcon.mInfo = modelHelper.bgDataModel.collections.valueAt(0) as FolderInfo
- folderIcon.mInfo.contents = folderItems
+ folderIcon.mInfo.getContents().addAll(folderItems)
+ // Use getAppContents() to "cast" contents to WorkspaceItemInfo so we can set bitmaps
+ val folderApps = modelHelper.bgDataModel.collections.valueAt(0).getAppContents()
// Set first icon to be themed.
- folderItems[0]
+ folderApps[0]
.bitmap
.setMonoIcon(
- folderItems[0].bitmap.icon,
+ folderApps[0].bitmap.icon,
BaseIconFactory(
context,
context.resources.configuration.densityDpi,
@@ -89,7 +91,7 @@
)
// Set second icon to be non-themed.
- folderItems[1]
+ folderApps[1]
.bitmap
.setMonoIcon(
null,
@@ -101,23 +103,21 @@
)
// Set third icon to be themed with badge.
- folderItems[2]
+ folderApps[2]
.bitmap
.setMonoIcon(
- folderItems[2].bitmap.icon,
+ folderApps[2].bitmap.icon,
BaseIconFactory(
context,
context.resources.configuration.densityDpi,
previewItemManager.mIconSize
)
)
- folderItems[2].bitmap =
- folderItems[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
+ folderApps[2].bitmap = folderApps[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
// Set fourth icon to be non-themed with badge.
- folderItems[3].bitmap =
- folderItems[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
- folderItems[3]
+ folderApps[3].bitmap = folderApps[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
+ folderApps[3]
.bitmap
.setMonoIcon(
null,
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index abb0c39..d3a6355 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -160,6 +160,6 @@
}
private List<WorkspaceItemInfo> allItems() {
- return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getContents();
+ return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getAppContents();
}
}
diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index ed587a1..2e209a4 100644
--- a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -168,8 +168,8 @@
val collections = modelHelper.getBgDataModel().collections
assertThat(collections.size()).isEqualTo(1)
- assertThat(collections.valueAt(0).contents.size).isEqualTo(itemCount)
- return collections.valueAt(0).contents
+ assertThat(collections.valueAt(0).getAppContents().size).isEqualTo(itemCount)
+ return collections.valueAt(0).getAppContents()
}
private fun verifyHighRes(items: ArrayList<WorkspaceItemInfo>, vararg indices: Int) {
diff --git a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
index 1002976..58b915f 100644
--- a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
+++ b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
@@ -40,14 +40,22 @@
import org.junit.Test
import org.junit.runner.RunWith
+private data class Grid(val tableName: String, val size: Point, val items: List<WorkspaceItem>) {
+ fun toGridState(): DeviceGridState =
+ DeviceGridState(size.x, size.y, size.x, InvariantDeviceProfile.TYPE_PHONE, tableName)
+}
+
@SmallTest
@RunWith(AndroidJUnit4::class)
class ValidGridMigrationUnitTest {
companion object {
const val SEED = 1044542
- const val REPEAT_AFTER = 10
+ val REPEAT_AFTER = Point(0, 10)
+ val REPEAT_AFTER_DST = Point(6, 15)
const val TAG = "ValidGridMigrationUnitTest"
+ const val SMALL_TEST_SIZE = 60
+ const val LARGE_TEST_SIZE = 1000
}
private lateinit var context: Context
@@ -57,14 +65,10 @@
context = InstrumentationRegistry.getInstrumentation().targetContext
}
- private fun validate(
- srcItems: List<WorkspaceItem>,
- dstItems: List<WorkspaceItem>,
- destinationSize: Point
- ) {
+ private fun validate(srcGrid: Grid, dstGrid: Grid, resultItems: List<WorkspaceItem>) {
// This returns a map with the number of repeated elements
// ex { calculatorIcon : 6, weatherWidget : 2 }
- val itemsToSet = { it: List<WorkspaceItem> ->
+ val itemsToMap = { it: List<WorkspaceItem> ->
it.filter { it.container != Favorites.CONTAINER_HOTSEAT }
.groupingBy {
when (it.type) {
@@ -77,34 +81,36 @@
}
.eachCount()
}
- for (it in dstItems) {
- assert((it.x in 0..destinationSize.x) && (it.y in 0..destinationSize.y)) {
- "Item outside of the board size. Size = $destinationSize Item = $it"
+ resultItems.forEach {
+ assert((it.x in 0..dstGrid.size.x) && (it.y in 0..dstGrid.size.y)) {
+ "Item outside of the board size. Size = ${dstGrid.size} Item = $it"
}
assert(
- (it.x + it.spanX in 0..destinationSize.x) &&
- (it.y + it.spanY in 0..destinationSize.y)
+ (it.x + it.spanX in 0..dstGrid.size.x) && (it.y + it.spanY in 0..dstGrid.size.y)
) {
- "Item doesn't fit in the grid. Size = $destinationSize Item = $it"
+ "Item doesn't fit in the grid. Size = ${dstGrid.size} Item = $it"
}
}
- assert(itemsToSet(srcItems) == itemsToSet(dstItems)) {
- "The srcItems do not match the dstItems src = $srcItems dst = $dstItems"
+ val srcCountMap = itemsToMap(srcGrid.items)
+ val resultCountMap = itemsToMap(resultItems)
+ val diff = resultCountMap - srcCountMap
+
+ diff.forEach { (k, count) ->
+ assert(count >= 0) { "Source item $k not present on the result" }
}
}
- private fun addItemsToDb(db: SQLiteDatabase, tableName: String, items: List<WorkspaceItem>) {
+ private fun addItemsToDb(db: SQLiteDatabase, grid: Grid) {
LauncherDbUtils.SQLiteTransaction(db).use { transaction ->
- items.forEach { insertIntoDb(tableName, it, transaction.db) }
+ grid.items.forEach { insertIntoDb(grid.tableName, it, transaction.db) }
transaction.commit()
}
}
private fun migrate(
- srcItems: List<WorkspaceItem>,
- srcSize: Point,
- targetSize: Point
+ srcGrid: Grid,
+ dstGrid: Grid,
): List<WorkspaceItem> {
val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
val dbHelper =
@@ -114,46 +120,64 @@
{ UserCache.INSTANCE.get(context).getSerialNumberForUser(it) },
{}
)
- val srcTableName = Favorites.TMP_TABLE
- val dstTableName = Favorites.TABLE_NAME
- Favorites.addTableToDb(dbHelper.writableDatabase, userSerial, false, srcTableName)
- addItemsToDb(dbHelper.writableDatabase, srcTableName, srcItems)
+
+ Favorites.addTableToDb(dbHelper.writableDatabase, userSerial, false, srcGrid.tableName)
+
+ addItemsToDb(dbHelper.writableDatabase, srcGrid)
+ addItemsToDb(dbHelper.writableDatabase, dstGrid)
+
LauncherDbUtils.SQLiteTransaction(dbHelper.writableDatabase).use {
GridSizeMigrationUtil.migrate(
dbHelper,
- GridSizeMigrationUtil.DbReader(it.db, srcTableName, context, MockSet(1)),
- GridSizeMigrationUtil.DbReader(it.db, dstTableName, context, MockSet(1)),
- targetSize.x,
- targetSize,
- DeviceGridState(
- srcSize.x,
- srcSize.y,
- srcSize.x,
- InvariantDeviceProfile.TYPE_PHONE,
- srcTableName
- ),
- DeviceGridState(
- targetSize.x,
- targetSize.y,
- targetSize.x,
- InvariantDeviceProfile.TYPE_PHONE,
- dstTableName
- )
+ GridSizeMigrationUtil.DbReader(it.db, srcGrid.tableName, context, MockSet(1)),
+ GridSizeMigrationUtil.DbReader(it.db, dstGrid.tableName, context, MockSet(1)),
+ dstGrid.size.x,
+ dstGrid.size,
+ srcGrid.toGridState(),
+ dstGrid.toGridState()
)
it.commit()
}
- return readDb(dstTableName, dbHelper.readableDatabase)
+ return readDb(dstGrid.tableName, dbHelper.readableDatabase)
}
@Test
fun runTestCase() {
val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
- for (i in 0..50) {
- val testCase = caseGenerator.generateTestCase()
+ for (i in 0..SMALL_TEST_SIZE) {
+ val testCase = caseGenerator.generateTestCase(isDestEmpty = true)
Log.d(TAG, "Test case = $testCase")
- val srcItemList = generateItemsForTest(testCase, REPEAT_AFTER)
- val dstItemList = migrate(srcItemList, testCase.srcSize, testCase.targetSize)
- validate(srcItemList, dstItemList, testCase.targetSize)
+ val srcGrid =
+ Grid(
+ tableName = Favorites.TMP_TABLE,
+ size = testCase.srcSize,
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ )
+ val dstGrid =
+ Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
+ validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
+ }
+ }
+
+ @Test
+ fun mergeBoards() {
+ val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
+ for (i in 0..SMALL_TEST_SIZE) {
+ val testCase = caseGenerator.generateTestCase(isDestEmpty = false)
+ Log.d(TAG, "Test case = $testCase")
+ val srcGrid =
+ Grid(
+ tableName = Favorites.TMP_TABLE,
+ size = testCase.srcSize,
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ )
+ val dstGrid =
+ Grid(
+ tableName = Favorites.TABLE_NAME,
+ size = testCase.targetSize,
+ items = generateItemsForTest(testCase.destBoards, REPEAT_AFTER_DST)
+ )
+ validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
}
}
@@ -162,12 +186,18 @@
@Test
fun runExtensiveTestCases() {
val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
- for (i in 0..1000) {
- val testCase = caseGenerator.generateTestCase()
+ for (i in 0..LARGE_TEST_SIZE) {
+ val testCase = caseGenerator.generateTestCase(isDestEmpty = true)
Log.d(TAG, "Test case = $testCase")
- val srcItemList = generateItemsForTest(testCase, REPEAT_AFTER)
- val dstItemList = migrate(srcItemList, testCase.srcSize, testCase.targetSize)
- validate(srcItemList, dstItemList, testCase.targetSize)
+ val srcGrid =
+ Grid(
+ tableName = Favorites.TMP_TABLE,
+ size = testCase.srcSize,
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ )
+ val dstGrid =
+ Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
+ validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
}
}
}
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index 43fc8ff..913dfa2 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -37,6 +37,7 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.After;
import org.junit.Before;
@@ -240,6 +241,7 @@
});
}
+ @ScreenRecordRule.ScreenRecord // b/329935119
@Test
@PortraitLandscape
public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
diff --git a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
index 0065527..dae09bb 100644
--- a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
+++ b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
@@ -139,9 +139,9 @@
@Test
fun test_folder_inflated_on_UI() {
val itemInfo = FolderInfo()
- itemInfo.contents.add(workspaceItemInfo())
- itemInfo.contents.add(workspaceItemInfo())
- itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
val view =
MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
@@ -155,9 +155,9 @@
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
val itemInfo = FolderInfo()
- itemInfo.contents.add(workspaceItemInfo())
- itemInfo.contents.add(workspaceItemInfo())
- itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
val view =
VIEW_PREINFLATION_EXECUTOR.submit(
@@ -173,8 +173,8 @@
fun test_app_pair_inflated_on_UI() {
val itemInfo = AppPairInfo()
itemInfo.itemType = ITEM_TYPE_APP_PAIR
- itemInfo.contents.add(workspaceItemInfo())
- itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
val view =
MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
@@ -189,8 +189,8 @@
val itemInfo = AppPairInfo()
itemInfo.itemType = ITEM_TYPE_APP_PAIR
- itemInfo.contents.add(workspaceItemInfo())
- itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
+ itemInfo.add(workspaceItemInfo())
val view =
VIEW_PREINFLATION_EXECUTOR.submit(
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 3f70bb9..68829e0 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -398,8 +398,9 @@
if (isTablet && Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
return false;
}
- if (!mLauncher.isAppPairsEnabled() && task.isTaskSplit()) {
- // Overview actions aren't visible for split screen tasks.
+ if (task.isTaskSplit() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
+ // Overview actions aren't visible for split screen tasks, except for save app pair
+ // button on tablets.
return false;
}
return true;